4

How can I use the iOS OSLog in Xamarin.iOS?

I did succeed in using NSLog as follows, but I see no way of setting the subsystem (to the bundle identifier) with NSLog so that I can use that to filter the logs in Console.app.

public class Logger
{
    #if DEBUG
    [DllImport(ObjCRuntime.Constants.FoundationLibrary)]
    private extern static void NSLog(IntPtr message);
    #endif

    public void WriteLine(string line)
    {
        #if DEBUG
        using (var nss = new NSString(line))
        {
            NSLog(nss.Handle);
        }
        #endif
    }
}
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
Harindaka
  • 4,658
  • 8
  • 43
  • 62

2 Answers2

5

In Xamarin.iOS, there is a static CoreFoundation.OSLog object called Default that one can use straight away which will log messages with the specified OSLogLevel argument such as OSLogLevel.Debug, OSLogLevel.Error etc. You could have a method in your Xamarin.iOS code that logs a message at the Debug level:

using CoreFoundation;
// ...

public void Write(string message)
{
    OSLog.Default.Log(OSLogLevel.Debug, message);
}

If you want to use the subsystem and category, you have to instantiate an instance of OSLog using the appropriate constructor and use that instance to log your messages. Presumably you'd want to hold a static reference to your created instance and use it like you'd use OSLog.Default.

public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    public static OSLog LoggerInstance;

    public override bool FinishedLaunching(UIApplication app, NSDictionary launchOptions)
    {
        LoggerInstance = new OSLog(subsystem: "subsystem", category: "category");
//...

public class SomeClass
{
    public void SomeMethod()
    {
        AppDelegate.LoggerInstance?.Log(OSLogLevel.Debug, "log message");
        // ...
    }

This will print a fairly nice message in the device log with a timestamp and showing the specified category.

bcr
  • 1,983
  • 27
  • 30
3

OSLog is a ObjC struct (of two const chars) and a kernel method, os_log_create, is provided to allocate one.

Note: Refer to the os/log.h for details.

Define:

[DllImport("__Internal", EntryPoint = "os_log_create")]
public static extern IntPtr os_log_create(string subsystem, string category);

Usage:

var oslog = os_log_create("some.bundle.id", "StackOverflowCategory");

FYI: your NSLog should include a printf format string as a NSString

[DllImport (Constants.FoundationLibrary, EntryPoint = "NSLog")]
extern static void NSLog (IntPtr format, [MarshalAs (UnmanagedType.LPStr)] string s);
SushiHangover
  • 73,120
  • 10
  • 106
  • 165
  • Thanks but how can I use the oslog variable to produce debug output? – Harindaka Sep 22 '18 at 17:18
  • @Harindaka You linked to the OSLog class/object (which is a Swift framework wrapper for `os_log_t`) so that is what I answered on. If you what to use the unified logging system and pass a `os_log_t` to it, I would recommend looking at `os/log.h`. `_os_log_impl` is the base method for unified logging (`_os_log_internal` is deprecated) and you can marshal its parameters from C# to ObjC. Again you should review os/log.h as most of it is just macros that wrap `os_log_type_enabled` and the low level `_os_log_impl` call. – SushiHangover Sep 22 '18 at 17:31
  • Sorry I'm not much of a swift/objc dev. I will have a look at that. I can see from this link (https://forums.xamarin.com/discussion/comment/227392) that there is a method which has the following signature: os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, "Hello os_log!"). Is this the same as the oslog variable above and if how can I import the consts OS_LOG_DEFAULT and OS_LOG_TYPE_DEFAULT or OS_LOG_TYPE_DEBUG/OS_LOG_TYPE_INFO, etc – Harindaka Sep 22 '18 at 17:35
  • I actually want to output debug messages – Harindaka Sep 22 '18 at 17:35
  • @Harindaka `os_log_with_type` is a just macro for `os_log_type_enabled` and `_os_log_impl` (this is defined in `os/log.h`) For a `os_log_type` of just `OS_LOG_TYPE_DEBUG` you can just use `Console.Writeline` But if you want control over the category, the bundle sub ids, etc... then `_os_log_impl` is the call you want as it is the kernel call that the ObjC macros and the Swift framework is using. – SushiHangover Sep 22 '18 at 17:40
  • @SushiHangover you mean `_os_log_internal`, I assume? – Sören Kuklau Mar 08 '20 at 17:13