Skip to end of metadata
Go to start of metadata

When developing enterprise applications, developers are often challenged to provide a way to monitor their applications easily. Some aspects to monitor include the application performance, notify if there are critical failures that need to be handled quickly, gathering information about user behavior within the application, etc.

Crosslight implements logging abstraction at its core which comprises of simple and easy-to-use API that you can quickly consume in your code.  The abstraction of the logging functionality from the log destination allows the application code to be consistent, completely location-independent and type-independent of the target logging store. You can also easily replace the logging framework without requiring changes to your application code. 

Changes to almost all of the parameters that control logging are made possible simply by changing the configuration after deployment and at run time. This means that administrators and operators can customize the logging behavior as they manage the application.

This page is comprised of two major sections. The first section explains the Crosslight logging concept including the abstraction and interface design. The other section explains the Crosslight Logging Framework which is the concrete implementation of the logging component.

If you're already familiar with the logging concept, you may jump directly to Enabling Crosslight Logging Framework to start implementing logging in your apps.

Introduction to Crosslight Logging Interface

Before learning more about Crosslight Logging Framework, let's take a look at the logging concept in general. At the core, Crosslight provides ILog object to perform logging. The ILog object can be resolved through ILogService. By default, Crosslight provides an empty ILog implementation, so you will have to specify the actual logging framework to use. Crosslight includes a concrete logging implementation, further called Logging Framework, which will be discussed later in this page. However, you can opt to use your own logging framework by creating your ILog object and it will still work seamlessly throughout your application code.

The following figures shows the structure of ILog and ILogService interfaces.

As seen in the above figures, Crosslight provides simple logging API through the ILog interface, allowing you to easily implement logging in your application without concerning the actual logging types or frameworks.

Per-class Logging

Crosslight's logging abstraction is modeled upon popular logging concept that favor per-class logging pattern over traditional generic logging which offer numerous advantages in terms of consistency and flexibility. Specifically, per-class logging means that one logger is associated to a class which have its category automatically set to the full qualified name of the class. This allows you to write logging in a very simple way such as without concerning the category or other tedious information typically required in traditional generic logger.

To understand how per-class logging works, see the following code example.

It is important to understand the essence of category in Crosslight logging, and understand how it relates to filters and source levels. The category in Crosslight is based on named hierarchy. A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.

The hierarchy works very much in the same way as the namespace and class hierarchy in .NET. This is very convenient as you will see soon.

As seen in the above code example, you obtained an instance of ILogService through the service provider, then get an instance of the ILog object by passing the current type to the GetLogger method. With the full class name automatically set as the log category, you can further filter out our log entry using partial namespace hierarchy.

For example, consider that you have the following classes as follow:

  • MyApplication.ViewModels.HomeViewModel
  • MyApplication.ViewModels.RegisterViewModel
  • MyApplication.ViewModels.LoginViewModel
  • MyApplication.Services.RegisterService
  • MyApplication.Services.LoginService

Consider that you are only interested to see the logs for LoginService class, you can simply filter the log category to MyApplication.Services.LoginService. Since the log filter supports named hierarchy, the logging will be performed on all classes under that specified filter. So if you are interested to see the logs for all services classes, you can filter the log category to MyApplication.Services. This will automatically include the log for RegisterService and LoginService since they are children of the MyApplication.Services log. Likewise, if you would like to see all logs of the entire application, you can filter the log category to just MyApplication .

In addition, you can also perform filtering on the source levels in conjunction with the named hierarchy specified in the category. This design allows the most flexible configuration for application logging, while provides friendly API that can be easily understand by developers, administrators and users. For more information about filtering, see Filtering Log Entries.

Log Source Levels

The ILog interface provides numerous common API for logging such as Debug , Info , Warn , Fatal , and Error. Each method is equivalently mapped to the source level assigned to a logger which is used particularly for filtering purpose.

Crosslight logging interface supports the following levels which are defined in order of increasing priority:

  • ALL
  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL
  • OFF

If a given logger is not assigned a level, then it inherits one from its closest ancestor with an assigned level. More specifically, the inherited level for a given logger X, is equal to the first non-null level in the logger hierarchy, starting at X and proceeding upwards in the hierarchy towards the root logger.

To ensure that all loggers can eventually inherit a level, the root logger always has an assigned level. The default value for the root logger is INFO.
It's important to understand that a source level always include the priority of the lower levels. For instance, the ERROR level automatically include logs written with Error and Fatal methods, whereas INFO level automatically include all logs written with Info, Warn, Error and Fatal methods. This intuitive design allows you, administrators or users to configure the log filtering to match their exact needs.

The following table shows the example of how filtering works with category and levels.

Category FilterLevel FilterLogged ClassesLogged Severity
RootINFOAll classes that don't have the parent specified explicitly in other filters.Info, Warn, Error, Fatal
MyApplication.ServicesERRORClasses within MyApplication.Services namespace will be logged.Error, Fatal
MyApplication.ViewModelsALLClasses within MyApplication.ViewModels namespace will be logged.Debug, Info, Warn, Error, Fatal
MyApplication.DataOFFClasses within MyApplication.Data namespace will not be logged.-

To learn more how to work with log filtering, see Filtering Log Entries.

View Model Integration

For easy logging, Crosslight ViewModelBase has integrated ILog by default, so performing log from the ViewModel can be simply done by accessing ILog object through the Logger property.

This will create a new log entry with severity set to Info, category set to ApplicationLoggingSamples.ViewModels.HomeViewModel and log message set to "My Log". The log output would look something like the following:

Retrieving ILog object from ILogService

To perform logging on classes that aren't derived from ViewModelBase, you can use the ILogService.GetLogger() to obtain an ILog instance. Note that you need to specify the Type to enable per-class logging concept. The following code shows you how to resolve ILog object from ILogService.

 

Configuring Log Writer

In the above sections, you have learned the basic concept and fundamentals of Crosslight logging. However, the logging examples above will not produce any output yet since there is no log writer configured.

Crosslight provides a concrete implementation of logging framework, further called  Crosslight Logging Framework , which includes a set of base classes for log writers and predefined trace listeners that you can quickly consume. The Crosslight Logging Framework is discussed in the section below.
The following code example shows the most simple way to configure a log writer using Crosslight Logging Framework. In essence, you simply need to register a log writer service, and then add a log writer to the service.

AppService.cs in Shared Project

For in-depth guide about log writer configuration, please see Enabling Crosslight Logging Framework.

 

Crosslight Logging Framework Overview

Building on the Crosslight logging interface, Crosslight Logging Framework provides a highly flexible and configurable solution  that allows you to create and store log messages in a wide variety of locations, categorize and filter messages, and collect contextual information useful for debugging and tracing as well as for auditing and general logging requirements.

This section is designated to provide conceptual information about the Crosslight Logging Framework. In most cases, you will not need to deal with the Logging Framework types directly in your application code. However, you will need to understand the components of Logging Framework in details if you are interested to create your own trace listener, or create advanced logging mechanism not currently available in the Logging Framework.

To learn how to implement logging right away, see Enabling Crosslight Logging Framework.

Building upon Crosslight Foundation, you can use Crosslight Logging Framework in any platforms including server side. The following figure shows the architecture overview of Crosslight Logging Framework.

The Crosslight Logging Framework consisted of the following key components:

To learn more how Crosslight Logging Framework works, and how it relates to the Crosslight logging abstraction, see the following illustration.

StageDescription
Creating LogEntry

User writes a new log using ILog API (e.g ILog .Info()). This will create a new LogEntry with per-class category and specific severity based on ILog API used by the user. The created LogEntry then passed to LogWriter to be processed.

Filtering LogEntryThe LogFilter will filter the LogEntry based on the configuration in the LogWriter. You can use RootLevelFilter, RootLevelCategories and additional filters to filter out your LogEntry. This mechanism is useful to turn on/off specific logs that you want to see later when the application goes to production phase.
Selecting LogSourceAfter the LogEntry passes the filter process, it will pass to all LogSource configured in the LogWriter. Each LogSource will determine whether it will accept the LogEntry based on the filter configuration in each LogSource.
Selecting TraceListeners

If the LogSource accepts the LogEntry it will pass the LogEntry to all TraceListeners registered in the LogSource. When it has arrived at the TraceListener, it will write the log to its designated target such as output window, file log, list collection (memory) or any custom implementation such as calling a Web Service or any other systems.

Enabling Crosslight Logging Framework

Enable logging in your application using Crosslight Logging Framework is easy and straightforward. You will need to reference the Intersoft.Crosslight.Logging assembly in your core project and Intersoft.Crosslight.Logging.{Platform} assembly in your platform specific project. Afterward, you will need to configure the LogWriter and then you can start logging immediately. To learn more how to enable logging in your application, see Enabling Crosslight Logging Framework.

Writing Log Entries

Writing log entry using Crosslight Logging Framework is very straightforward, as all the heavy-lifting is done behind the scene in the logging framework implementation. You will only need to use the ILog object or Logger object that already available in ViewModelBase. If you want to log from classes that aren't derived from ViewModelBase, you can resolve the ILog object using ILogService provided by Crosslight Logging Framework. With this ILog object, you can start writing your log entries. To learn more how to write log entries, see Writing Log Entries.

Filtering Log Entries

A good way to log your application is to log everything at critical entry points that may have valuable information for the application, since excessive logging might slow down your application, so log sparingly.

With Crosslight Logging Framework, it is very easy to configure which log entry should be logged at any given time by using log filter capability. This gives your application's administrator a flexible way to turn on/off specific logging within your application. You can filter based on severity, category, priority and any other custom implementation.

To learn more how this filtering process works, see Filtering Log Entries.

Configuring Log Sources and Trace Listeners

Log sources and trace listeners define a set of rules and action for the log entries that passed the filtering process. Defining a log source means defining which log entry should be processed by the log source and defining which trace listeners are going to be used to process the log entry. In general, you create several log source to categorize how you want to handle the log entries. You might use the default log source to streamline all your log to file repository, but you might want to have all log with critical error send directly to your administrator mail. To do this you need to create register a new log source that will process all the critical log.

To learn more how to configure log source and trace listeners, see Configuring Log Sources and Trace Listeners.

Using Log Formatter

There are lots of information stored in LogEntry such as message, category, severity, timestamp, priority, event id and several more. Printing out all of these information in your output window or any other repository might make the log entry is not easily readable. By default Crosslight Logging Framework provides format string output for LogEntry for better readability. However you can further customize the format of your log entry using ILogFormater.

To learn more see, Using Log Formatter.

Enabling Entity Framework Application Logging

Crosslight Logging Framework is easily extensible, as the log sources and trace listeners are the pairs that actually writing the log to specific media. You can create different kind of log sources and trace listener to handle the log entries. Let's say you want to log your data to database. What you need to do is create a new log source and trace listener that handles writing the log entry to database. Seeing that logging to database is a pretty common case, Crosslight Logging Framework provides built-in capabilities to write the log entry to a database through Microsoft Entity Framework utilizing the extensible Crosslight Logging Framework.

To learn more how to write your log entry to a database, see Enabling Entity Framework Application Logging.

Enabling Azure Application Insights Logging

One of the purpose of logging is monitoring your application for several aspects, such as application performance, errors, user behaviors and many more. Azure Application Insights provides a repository that holds these kind of logs and presents them in Azure Application Insights reporting tools, so you can easily monitor your application at real time. Crosslight Logging Framework also provides another extension that allows you to log your application logs to Azure Application Insights service.

To learn more how to log your application log to Azure Application Insights service, see Enabling Azure Application Insights Logging.

Implementing Semantic Logging Design Pattern

Writing logs in your application might be a tedious work if not properly managed. One way to organize your logging code is by applying semantic logging design pattern. Semantic logging design pattern provides a centralized event source where you handle all your logging, so you can see the logs that are being logged in your application and how the log is executed all in one class. Another benefit of using semantic logging design pattern is eliminating code redundancy.

To lean more how to implement semantic logging design pattern, see Implementing Semantic Logging Design Pattern.

Samples

To learn more about application logging in Crosslight and see how it works, please checkout the following samples: