Press ESC to close · Ctrl+K to open

AVEVA ArchestrA – Write to the Logger from C#

Ready-to-use C# class wrapping LoggerDLL.dll to post Info, Warning, Error and Trace messages directly into the AVEVA System Platform and InTouch Logger.

AVEVA ArchestrA – Write to the Logger from C#

Índice

What is the ArchestrA Logger?

The ArchestrA Logger is the centralised logging system of AVEVA System Platform and InTouch. All platform components (Application Server, Historian, InTouch, etc.) write here and messages can be reviewed with ArchestrA Log Viewer.

When developing a Script Extension (a .NET library that extends Application Server objects) or any external component, integrating with this Logger keeps all traces in one place. That is where LoggerDLL.dll comes in.

ArchestrA Log Viewer with log level checkboxes enabled

When developing a .NET library or a new .NET component that extends functionality for the Application Server / InTouch, the cleanest approach is to integrate with this Logger instead of writing to a proprietary file. For this we use the library that already handles this for all platform components: LoggerDLL.dll.

Below are the methods we will use — this will help you understand the C# class better:

Exported methods from LoggerDLL.dll: REGISTERLOGGERCLIENT, LOGINFO, LOGWARNING, LOGERROR, LOGTRACE

Create the project Class Library (.NET Framework)

We create a Class Library (.NET Framework) project in Visual Studio. The framework must be compatible with the installed AVEVA version (currently .NET Framework 4.8).

Below is an example of one function inside the library we are building, where the key part is the call to the class we are about to create to write to the Logger:

Selección del tipo de proyecto y framework

The AvevaLogger class

Drop this class into your project, add a reference to LoggerDLL.dll (found at C:\Program Files (x86)\Common Files\ArchestrA\LoggerDLL.dll), and you get four log levels as static methods.

The class self-initialises on first call (lazy init), registers the client using the assembly name as identity, and automatically unregisters on process exit via the ProcessExit event.

csharp
using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace PHSTraining
{
    internal class AvevaLogger
    {
        private const string LoggerDllPath = @"C:\Program Files (x86)\Common Files\ArchestrA\LoggerDLL.dll";
        private static int _hIdentity = 0;
        private static bool _initialized = false;
        private static string _identityName = string.Empty;

        static AvevaLogger()
        {
            AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
        }

        [DllImport(LoggerDllPath, EntryPoint = "REGISTERLOGGERCLIENT")]
        private static extern int RegisterLoggerClient(ref int hIdentity);

        [DllImport(LoggerDllPath, EntryPoint = "UNREGISTERLOGGERCLIENT")]
        private static extern int UnregisterLoggerClient(ref int hIdentity);

        [DllImport(LoggerDllPath, EntryPoint = "SETIDENTITYNAME", CharSet = CharSet.Unicode)]
        private static extern int SetIdentityName(int hIdentity, string identityName);

        [DllImport(LoggerDllPath, EntryPoint = "LOGINFO", CharSet = CharSet.Unicode)]
        private static extern void LogInfo(int hIdentity, string message);

        [DllImport(LoggerDllPath, EntryPoint = "LOGWARNING", CharSet = CharSet.Unicode)]
        private static extern void LogWarning(int hIdentity, string message);

        [DllImport(LoggerDllPath, EntryPoint = "LOGERROR", CharSet = CharSet.Unicode)]
        private static extern void LogError(int hIdentity, string message);

        [DllImport(LoggerDllPath, EntryPoint = "LOGTRACE", CharSet = CharSet.Unicode)]
        private static extern void LogTrace(int hIdentity, string message);


        public static void LogInfo(string message)
        {
            EnsureInitialized();
            LogInfo(_hIdentity, message ?? string.Empty);
        }

        public static void LogWarning(string message)
        {
            EnsureInitialized();
            LogWarning(_hIdentity, message ?? string.Empty);
        }

        public static void LogError(string message)
        {
            EnsureInitialized();
            LogError(_hIdentity, message ?? string.Empty);
        }

        public static void LogTrace(string message)
        {
            EnsureInitialized();
            LogTrace(_hIdentity, message ?? string.Empty);
        }


        private static void EnsureInitialized()
        {
            if (_initialized) return;

            _identityName = Assembly.GetExecutingAssembly().GetName().Name;

            int rc = RegisterLoggerClient(ref _hIdentity);
            if (rc <= 0 || _hIdentity == 0)
                throw new InvalidOperationException("REGISTERLOGGERCLIENT failed.");

            rc = SetIdentityName(_hIdentity, _identityName);
            if (rc <= 0)
                throw new InvalidOperationException("SETIDENTITYNAME failed.");

            _initialized = true;
        }

        private static void OnProcessExit(object sender, EventArgs e)
        {
            if (_hIdentity != 0)
                UnregisterLoggerClient(ref _hIdentity);

            _hIdentity = 0;
            _initialized = false;
            _identityName = string.Empty;
        }
    }
}

After adding the class to your project, finish your library / User Control, compile it and import it into System Platform.

Referencia a LoggerDLL.dll añadida al proyecto

Once imported, you will see it has been copied to:
C:\Program Files (x86)\ArchestrA\Framework\FileRepository\[GalaxyName]\Vendors\ArchestrA

Carpeta Vendors\ArchestrA donde se despliega la Script Extension

Once imported, you can also check at the following path:
C:\Users\Administrator\AppData\Local\Temp\ScriptTemp
where the .aaSLIB and XML are automatically generated.

Carpeta Vendors\ArchestrA donde se despliega la Script Extension
Referencia a LoggerDLL.dll añadida al proyecto

Now we just need to test and implement it where appropriate. Here is the example:
A Script triggered with the Object Viewer.

Test script triggered from the Object Viewer

And the output in the Log Viewer :-)

Propiedades del proyecto: Output path apuntando a la carpeta temporal de Script Extension

Acknowledgements

Last but not least:

«Gratitude in silence serves no one.» — Gladys Berthe Stern

I want to thank my colleague Ivan Comino Martinez. These are the paradoxes of fate:

«When I worked at Telstar (Syntegon), a big hug to everyone reading this, Ivan Comino worked at Systel, and on more than one occasion I came across code and programs they had developed — in the comments the first thing I read was "IC" — and I thought... What great work! And now I am lucky enough to have him as a colleague, to learn from him and work as a team. These are the paradoxes of fate.» — José Manuel Luque