Android Intent BroadcastReceiver Example - c#

I am using a Zebra Android Scan-Gun and need to capture the hard-keyboard (not soft) keystrokes for P1, P2, F1, F2, etc... keys. Since Maui doesn't have events for KeyUp/KeyDown/KeyPressed there is nothing to inform me of a special keys being pressed. Not to mention I also have to put an Entry field on screens where there are only buttons because you can't catch a TextChanged event without an Entry.
My current solution involves Zebra StageNow to remap the P1 key to the "+" key and then strip it out of the Entry/TextBox (which is not a very elegant solution)
What I'm trying now is using the Windows app Zebra StageNow in Admin mode to remap the F11 key to send an Intent, OnKeyUp, Broadcast as seen in this screenshot.
Then I generate the bar-codes to program the scan-gun.
In the Android Scan-gun I then run StageNow and scan the bar-codes which programs the F11 key.
Then within my android app I added a class in the Platforms\Android folder called SpecialKeypress.cs
namespace myapp.Platforms.Android
{
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { "com.mycompany.myapp.F11" })]
public class SpecialKeypress : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var value = intent.GetStringExtra("Key"); // <-- I set a breakpoint here
throw new NotImplementedException();
}
}
}
Then I added the following to my AndroidManifest.xml
<application android:allowBackup="true" android:icon="#mipmap/appicon" android:roundIcon="#mipmap/appicon_round" android:supportsRtl="true">
<activity android:exported="true" android:name="com.mycompany.myapp.F11" >
<intent-filter>
<action android:name="com.mycompany.myapp.F11" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
I debug the app on the device and press F11 but alas my breakpoint is not hit in the OnReceive function.
I expect I'm missing something fundamental but I tried to keep it as simple as possible for this example. Also, there are no good examples of how to do this with Maui on the internet (yet).

You may need to register a receiver in your AndroidManifest.xml, and put some intent filters that define what type of messages this receiver will get.
For example:
<receiver android:name=".SpecialKeypress">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>

Related

Unable to find explicit activity class when open deeplink

I had Xamarin.Android App with deeplinks on API32. After I updated API to 33, Deeplinks has broken. I had this exception in my logs:
Time Device Name Type PID Tag Message
02-14 08:25:48.878 OnePlus IN2015 Error 16453 AcceptInvitation android.content.ActivityNotFoundException: Unable to find explicit activity class {PACKAGENAME/crc645ff435851f1c2612.MainActivity}; have you declared this activity in your AndroidManifest.xml, or does your intent not match its declared <intent-filter>?
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2184)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1831)
at android.app.Activity.startActivityForResult(Activity.java:5555)
at hcm.platform_startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcl.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at com.google.android.chimera.android.Activity.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hgx.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcl.public_startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcm.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):3)
at android.app.Activity.startActivityForResult(Activity.java:5508)
at hcm.platform_startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at hcl.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at com.google.android.chimera.android.Activity.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at hgx.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at hcl.public_startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at hcm.startActivityForResult(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at android.app.Activity.startActivity(Activity.java:6011)
at hcm.platform_startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcl.startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at com.google.android.chimera.android.Activity.startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcl.public_startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):2)
at hcm.startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):3)
at android.app.Activity.startActivity(Activity.java:5978)
at hcm.platform_startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at hcl.startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at com.google.android.chimera.android.Activity.startActivity(:com.google.android.gms#230413045#23.04.13 (190408-505809224):1)
at kmv.x(:com.google.android.gms#230413045#23.04.13 (190408-505809224):4)
at kmw.onPostExecute(:com.google.android.gms#230413045#23.04.13 (190408-505809224):3)
at android.os.AsyncTask.finish(AsyncTask.java:771)
at android.os.AsyncTask.-$$Nest$mfinish(Unknown Source:0)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:240)
at android.os.Looper.loop(Looper.java:351)
at android.app.ActivityThread.main(ActivityThread.java:8355)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
I tried to google a solution to this problem, tried a lot:
Changed ProGuard settings,
I checked various variations of calling MainActivity with the name of the assembly and just with a dot,
Updated my libraries and SDK,
I tried to prescribe other activities,
I checked the assemblies work both in debug and in release,
Analyzed dex files.
Nothing really helps.
Own analysis showed that the problem is that there are two MainActivities in
the list of activities.
I am also attaching AndroidManifest:
...
<application android:requestLegacyExternalStorage="true"
android:allowBackup="true" android:icon="#mipmap/icon"
android:largeHeap="true" android:supportsRtl="true"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:localeConfig="#xml/locales_config"
android:name=".MyApplication">
...
<activity android:name=".MainActivity" android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="SITE.com" />
<data android:host="OWN.LINK" />
</intent-filter>
</activity>
...
</application>
MainActivity class has MainLauncher attribute.
Help me please! I spent a day on this and I'm afraid that without help I can be stuck for a long time. Seems like the error might be obvious.
[Activity(ScreenOrientation = ScreenOrientation.Portrait, NoHistory = true, MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
...
}
UPD:
Sometimes I had this exception:
Time Device Name Type PID Tag Message
02-14 08:38:29.150 OnePlus IN2015 Info 28812 MonoDroid Caused by: java.lang.ClassNotFoundException: Didn't find class "PACKAGENAME.MainActivity" on path: DexPathList[[zip file "/data/app/~~jXWlqXqgllqjZ1bi2qN5dg==/PACKAGENAME-gqSZGEIemXo6mh-lUysz-w==/base.apk"],nativeLibraryDirectories=[/data/app/~~jXWlqXqgllqjZ1bi2qN5dg==/PACKAGENAME-gqSZGEIemXo6mh-lUysz-w==/lib/arm64, /data/app/~~jXWlqXqgllqjZ1bi2qN5dg==/PACKAGENAME-gqSZGEIemXo6mh-lUysz-w==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(SourceFile:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1347)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3745)
... 12 more
From the generated Manifest.xml file you posted, I couldn't find the following intent-filter:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
so, you can recheck if you have set the MainLauncher = true correctly for your Activity.
You can refer to my setting,just as follows:
[Activity(Label = "SearchBarDemos", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true,Exported =true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
[
IntentFilter
(
new[] { Android.Content.Intent.ActionView },
Categories = new[]
{
Android.Content.Intent.CategoryDefault,
Android.Content.Intent.CategoryBrowsable
},
DataSchemes = new[] { "myapp" }
)
]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
}
And you can refer to a similar thread here: Navigate from one app to another using Xamarin.
Note:
If you have set ActivityAttribute MainLauncher = true for your MainActivity, you don't need to set it on fileManifest.xml,which easily leads to generating two MainActivitys you said.
On other words,you don't need to add any code into the generating Manifest.xml file.
For more information,please check:Working with the Android Manifest.
The "ActivityNotFoundException" error generally occurs when an Android app is trying to launch an Activity using an Intent, but the specified Activity class cannot be found or does not exist. You can try below some hit and trial methods:
Issue can be because of incorrect activity class name so make sure that the activity class name in your Intent is correct, including the package name.
Also make sure that the activity class must be declared in your AndroidManifest.xml file, or it will not be recognized by the Android system.
If the Activity class is present in your code, but you still encountering this error, it could be because the Activity class is not being included in your APK file. This can happen if the class is not being referenced properly in your code, or if the build process is excluding the class from the APK.
If the Activity class is being launched through a deep link, just make sure that the correct Intent filters are set up for the Activity in your AndroidManifest.xml file.
It's also possible that the update from API 32 to API 33 could have caused changes in the Android framework that are affecting your deep links. You may want to check the Android Developer documentation for any changes in the way that deep links are handled in API 33, and make any necessary updates to your code accordingly.
I hope these findings may help you out to rectify your doubt.
Thanks.

Xamarin C# , I Cant Run Plugin.BLE Codes

I'm trying to make a very simple and basic mobile app for controlling my Arduino with Bluetooth.
Don't have many C# experiences, but it was going well until the Bluetooth part comes.
I've searched everywhere and found this https://github.com/xabre/xamarin-bluetooth-le
and started to do instructions in the readme. But I've got errors every time.
first, I copied these codes to androidmanifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
after that, I added these codes to mainpage.xml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var ble = CrossBluetoothLE.Current;
var adapter = CrossBluetoothLE.Current.Adapter;
var state = ble.State;
}
}
But that caused 2 errors: Error CS0103 The name 'CrossBluetoothLE' does not exist in the current context BlueT
and I added using Plugin.BLE; so errors disappeared. and I continued to write;
adapter.DeviceDiscovered += (s,a) => deviceList.Add(a.Device);
await adapter.StartScanningForDevicesAsync();
after I paste these, I got that errors:
Error CS4033 The 'await' operator can only be used within an async
method. Consider marking this method with the 'async' modifier and
changing its return type to 'Task
Error CS0103 The name 'deviceList' does not exist in the current
context
I tried everything but couldn't solve this problem, and I'm sorry like I said, don't have many # or Android programming experiences, just want to send these slider and button data to my Arduino:
This is my mobile controller app

Xamarin run activity from other project in same solution

I have a xamarin solution with two android projects. I would like to run android activity in project1 from project2. I ‘ve seen threads related to this topic (like How to call an activity in another project?
Call activity from another project) and tried many suggested solutions but In the end I always get „Android.Content.ActivityNotFoundException: 'Unable to find explicit activity class {companyname.project2/companyname.project2.Droid.View.LoginView}; have you declared this activity in your AndroidManifest.xml?'” error. Is this possible in xamarin if yes how? Am I doing something wrong? Here is my source code:
Project1:
namespace BigAppManager {
    [Activity (MainLauncher = true, Name = "com.companyname.bigappmanager.MainActivity", Label = " Logowanie", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, NoHistory = true, LaunchMode = LaunchMode.SingleInstance)]
    public class MainActivity : MvxActivity<MainActivityViewModel> {
        protected override void OnCreate (Bundle savedInstanceState) {
            base.OnCreate (savedInstanceState);
            Xamarin.Essentials.Platform.Init (this, savedInstanceState);
            
            SetContentView (Resource.Layout.activity_main);
            Button button = FindViewById<Button> (Resource.Id.button);
            button.Click += ClickButton;
        }
        private void ClickButton (object sender, EventArgs e) {
  
            //Intent intent = new Intent("companyname.project2.Droid.View.LoginView");
            //StartActivity(intent);
            Intent intent = new Intent (Intent.ActionMain);
            intent.AddCategory (Intent.CategoryLauncher);
            // intent.SetClassName("companyname.project2", "companyname.project2.Droid.View.LoginView");
            intent.SetComponent (new ComponentName ("companyname.project2", "companyname.project2.Droid.View.LoginView"));
            StartActivity (intent);
        }
        public override void OnRequestPermissionsResult (int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult (requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult (requestCode, permissions, grantResults);
        }
    }
}
Project2:
    
[Activity(Name = "companyname.project2.Droid.View.LoginView", Label = " Logowanie", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, NoHistory = true, LaunchMode = LaunchMode.SingleInstance)]
    public class LoginView : MvxActivity<LoginViewModel>
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            SetContentView(Resource.Layout.LoginView);
        }
    }
Project 2 android manifest intent filter:
<activity android:name="companyname.project2.Droid.View.LoginView">
<intent-filter>
<action android:name="companyname.project2.View.LoginView"></action>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
I don't think Manifests are merged by default. So if you look at the resulting AndroidManifest.xml in obj/Debug/android when building, you will probably notice that the LoginView entry is missing.
You can try adding the following NuGet package to your App project and see if that will merge the manifests: https://www.nuget.org/packages/Xamarin.Android.ManifestMerger/1.0.0-preview03
Otherwise, you will need to add the same entry manually in your Apps manifest for the LoginView.
EDIT:
Make sure you use the correct intent. This works fine for me:
var intent = new Intent(this, typeof(LoginView));

Xamarin.Android - Getting Location Providers returns empty every time

I'm learning to develop for Android through Xamarin right now and I'm following this basic tutorial on Xamarin's website. I created a class-level variable LocationManager _mgr; and call InitializeLocationManager(); inside of my OnCreate(Bundle bundle) method. InitializeLocationManager() is implemented as follows:
public void InitializeLocationManager()
{
_mgr = (LocationManager)GetSystemService(LocationService);
Criteria locCriteria = new Criteria { Accuracy = Accuracy.Coarse, PowerRequirement = Power.Medium };
IList<string> acceptableLocationProviders = _mgr.GetProviders(false);
if (acceptableLocationProviders.Any())
{
locationProvider = acceptableLocationProviders.First();
}
else
{
locationProvider = string.Empty;
}
Console.WriteLine($"Using {locationProvider} as our location provider.");
}
The line Criteria locCriteria = new Criteria { Accuracy = Accuracy.Coarse, PowerRequirement = Power.Medium }; differs from the tutorial because along my troubleshooting journey, someone said that it worked for them. When I simply use Accuracy = Accuracy.Fine I get the same exact results.
The line that is written to the console every single time is Using as our location provider", obviously meaning the empty string.
The class (MainActivity) extends from Activity and implements ILocationListener correctly. The codefile is declared to use System.Linq, so IList methods .Any() and .First() both work correctly.
Why is this not working? I've followed the tutorial nearly identically, save for some variable names. I have ensured that I have location turned on with my testing device. I am not on WiFi, and when I was, it still did not work. I'm on 4G LTE, and it does not work.
Any ideas would be much appreciated.
Thanks so much.
EDIT: UPDATE: I added the following piece of code to my OnCreate() method:
if( ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) == Android.Content.PM.Permission.Granted )
{
Console.WriteLine($"We've got permission!");
}
else
{
Console.WriteLine($"We don't have permission you idiot!");
}
And the line written is We don't have permission you idiot! every time. My AndroidManifest.xml has these lines:
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
<uses-feature android:name="android.hardware.location.gps" />
<application android:allowBackup="true" android:label="#string/app_name"></application>
My AssemblyInfo.cs file has these lines, just like the tutorial:
[assembly: UsesPermission(Manifest.Permission.Internet)]
[assembly: UsesPermission(Manifest.Permission.AccessFineLocation)]
[assembly: UsesPermission(Manifest.Permission.AccessCoarseLocation)]
My application does not request permissions on startup. What is going on?
If LocationManager.GetProviders returns an empty Java ArrayList when requesting provider even if they are turned off/unavailable, then you have not requested permission to course|fine location.
First, either manually add the permission to the manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Or even better, use the Build / Android application / Required Permissions options panel:
Second, if targeting Marshmallow/API-23 or later, make sure you are requesting runtime location permission (and still include the permission entries in the manifest).
https://blog.xamarin.com/requesting-runtime-permissions-in-android-marshmallow/
Note: That you are not using the location criteria that you are defining, you need pass it into the GetProviders method:
IList<string> acceptableLocationProviders = _mgr.GetProviders(locCriteria, false);

How to dynamically set log file using App.config and System.Diagnostics?

I was looking for a solution to provide logging to my latest project when I came across an article ( http://www.daveoncsharp.com/2009/09/create-a-logger-using-the-trace-listener-in-csharp/ ) that talked about using System.Diagnostics and App.config to log via the Trace method. I was able to successfully implement both the class and the App.config but I'd really like to be able to dynamically assign the value/location (inside initializeData) of the log file and I don't have a clue how to do it. If you have any suggestions, please feel free to post!
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="myListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="fileSizeThreshold=512, fileSizeUnit=kilobytes,
fileAgeThreshold=1, fileAgeUnit=months, fileNameTemplate='{0}\MyApp-{1:MMM-yy}.log'"/>
<remove name="Default" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
Logger Class:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace MyCurrentProject
{
class Logger
{
public void Error(string module, string message)
{
WriteEntry(message, "ERROR:", module);
}
public void Error(Exception ex, string module)
{
WriteEntry(ex.Message, "ERROR:", module);
}
public void Warning(string module, string message)
{
WriteEntry(message, "WARNING:", module);
}
public void Info(string module, string message)
{
WriteEntry(message, "INFO:", module);
}
private void WriteEntry(string message, string type, string module)
{
Trace.WriteLine(
string.Format("{0} {1} [{2}] {3}",
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
type,
module,
message));
}
}
}
RE: Sorry for not being clear... just to be clear, I need the file path that the log file output is save to to be dynamically set. I'd like the path to save to a location in %AppData%. The problem I had with the config was that once I set the value for 'initializeData' I couldn't find a way to change or dynamically set/reset that value. Truthfully... at this point I just want a solution that works and allows me to manage the location of the log file.
Here is a worked example using Systems.Diagnostics, which has two advantages over non-base class libraries. It is always allowed (in large organizations), always available, and to the extent that developers are familiar with the base class library, the next batch of maintenance developers will have heard of it.
using System.Diagnostics;
namespace DynamicTraceLog
{
class Program
{
static void Main(string[] args)
{
//Use TraceSource, not Trace, they are easier to turn off
TraceSource trace = new TraceSource("app");
//SourceSwitches allow you to turn the tracing on and off.
SourceSwitch level =new SourceSwitch("app");
//I assume you want to be dynamic, so probalby some user input would be here:
if(args.Length>0 && args[0]=="Off")
level.Level= SourceLevels.Off;
else
level.Level = SourceLevels.Verbose;
trace.Switch = level;
//remove default listner to improve performance
trace.Listeners.Clear();
//Listeners implement IDisposable
using (TextWriterTraceListener file = new TextWriterTraceListener("log.txt"))
using (ConsoleTraceListener console = new ConsoleTraceListener())
{
//The file will likely be in /bin/Debug/log.txt
trace.Listeners.Add(file);
//So you can see the results in screen
trace.Listeners.Add(console);
//Now trace, the console trace appears immediately.
trace.TraceInformation("Hello world");
//File buffers, it flushes on Dispose or when you say so.
file.Flush();
}
System.Console.ReadKey();
}
}
}
Re: how to format the output
Try either of these two for trace listeners that implement templated trace formating/output using Systems.Diagnostics classes:
http://essentialdiagnostics.codeplex.com
or http://ukadcdiagnostics.codeplex.com/
Systems.Diagnostics doesn't provide for any particular formating or outputing of standard tokens.
I know I may get down voted for this, but I'm going to go off the ranch for a minute and I'm going to suggest you use a different tool from the toolbox. Log4Net is a very powerful and succinct logging framework. For example, below is a console application that uses it and it's fully functional as you see it.
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// Set up a simple configuration that logs on the console.
BasicConfigurator.Configure();
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
But let's say we wanted to do that with a configuration file just like you're implying you would like to use. Well, that's pretty straight forward! Below is the configuration we'd place in the App.config to accomplish the same thing:
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="DEBUG" />
<appender-ref ref="A1" />
</root>
</log4net>
Then that configuration can be used like this:
using Com.Foo;
// Import log4net classes.
using log4net;
using log4net.Config;
public class MyApp
{
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
static void Main(string[] args)
{
// BasicConfigurator replaced with XmlConfigurator.
XmlConfigurator.Configure(new System.IO.FileInfo(args[0]));
log.Info("Entering application.");
Bar bar = new Bar();
bar.DoIt();
log.Info("Exiting application.");
}
}
And don't let the pattern stuff catch you off guard, it's just configuring what you're coding above so that you can have some consistency in the messages. It really keeps it easy though because all you ever have to log is the information that needs to be plugged into the pattern and the pattern is then encapsulated away.
This is a crash course in Log4Net, but the reason I'm really recommending it is because in the two examples above you see that they logged to the console, but you have a myriad of possible loggers, just look at this list:
log4net.Appender.AdoNetAppender: Writes logging events to a database using either prepared statements or stored procedures.
log4net.Appender.AnsiColorTerminalAppender: Writes color highlighted logging events to a an ANSI terminal window.
log4net.Appender.AspNetTraceAppender: Writes logging events to the ASP trace context. These can then be rendered at the end of the ASP page or on the ASP trace page.
log4net.Appender.ColoredConsoleAppender: Writes color highlighted logging events to the application's Windows Console.
log4net.Appender.ConsoleAppender: Writes logging events to the application's Console. The events may go to either the standard our stream or the standard error stream.
log4net.Appender.DebugAppender: Writes logging events to the .NET system.
log4net.Appender.EventLogAppender: Writes logging events to the Windows Event Log.
log4net.Appender.FileAppender: Writes logging events to a file in the file system.
log4net.Appender.LocalSyslogAppender: Writes logging events to the local syslog service (UNIX only).
log4net.Appender.MemoryAppender: Stores logging events in an in memory buffer.
log4net.Appender.NetSendAppender: Writes logging events to the Windows Messenger service. These messages are displayed in a dialog on a users terminal.
log4net.Appender.OutputDebugStringAppender: Writes logging events to the debugger. If the application has no debugger, the system debugger displays the string. If the application has no debugger and the system debugger is not active, the message is ignored.
log4net.Appender.RemoteSyslogAppender: Writes logging events to a remote syslog service using UDP networking.
log4net.Appender.RemotingAppender: Writes logging events to a remoting sink using .NET remoting.
log4net.Appender.RollingFileAppender: Writes logging events to a file in the file system. The RollingFileAppender can be configured to log to multiple files based upon date or file size constraints.
log4net.Appender.SmtpAppender: Sends logging events to an email address.
log4net.Appender.SmtpPickupDirAppender: Sends logging events to an email address but writes the emails to a configurable directory rather than sending them directly via SMTP.
log4net.Appender.TelnetAppender: Clients connect via Telnet to receive logging events.
log4net.Appender.TraceAppender: Writes logging events to the .NET trace system.
log4net.Appender.UdpAppender: Sends logging events as connectionless UDP datagrams to a remote host or a multicast group using a UdpClient.
So, as you can see, it's extremely capable OOB. I hope this post has been helpful.

Categories