Configuration
Example use in a console app
Create an appender with the following XML configuration:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="CloudLogger" type="Google.Cloud.Logging.Log4Net.GoogleStackdriverAppender,Google.Cloud.Logging.Log4Net">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message" />
</layout>
<projectId value="PROJECT_ID" />
<logId value="LOG_ID" />
</appender>
<root>
<level value="ALL" />
<appender-ref ref="CloudLogger" />
</root>
</log4net>
Save this as log4net.xml
in your application directory.
Then load it during program startup:
using log4net;
using log4net.Config;
public static void Main(string[] args)
{
XmlConfigurator.Configure(new FileInfo("log4net.xml"));
}
Logged items will now be sent to Google Cloud Logging:
using log4net;
ILog log = LogManager.GetLogger(typeof(Program));
log.Info("An exciting log entry!");
Custom configuration
Within a GoogleStackdriverAppender
the following options are
available (see also the GoogleStackdriverAppender reference documentation
for more details):
projectId
<projectId value="<your project ID>"/>
Optional when running on Google App Engine (GAE) or Google Compute Engine (GCE), must be present on other platforms. The project ID given must be the ID of a project that already exists on Google Cloud Platform.
On GCE and GAE the project ID will be auto-detected from the platform.
logId
<logId value="<your log ID>"/>
Must be present. The log ID given is any string that is recorded as the name of the log, which can be used for simple filtering when viewing log entries.
withMetaData
<withMetaData value="<various, see below>"/>
By default no extra metadata is logged. withMetaData
specifies which extra metadata to log with each log entry, and can be specified multiple times to enable multiple type of metadata:
Location
: The class name, file name, and line number of the log call.Identity
: The identity of the current thread principle.ThreadName
: The name of the thread that performed the log call.UserName
: The name of the current user.Domain
: The AppDomain friendly name.LoggerName
: The name of the log4net logger that created this log entry.Label
: The name of the log4net logging level that caused this log entry (e.g. "FATAL")
customLabel
<customLabel>
<key value="<your key>" />
<value value="<your value>" />
</customLabel>
Allows custom labels to be added to the metadata of log entries.
UsePatternWithinCustomLabels
<usePatternWithinCustomLabels value="true"/>
Defaults to false
. Setting this to true
enables PatternLayout
use in custom labels.
All the standard patterns documented for Log4Net PatternLayout
are available.
Custom pattern conversions are not possible.
resourceType
<resourceType value="<your resource type>"/>
Allows the Monitored resources resource type to be specified; must be set to an entry on the Monitored Resource List.
If unset, the default value depends on the detected platform:
gae_app
on Google App Engine (GAE).gce_instance
on Google Compute Engine (GCE).global
on all other platforms.
(See MonitoredResourceBuilder.cs for details)
resourceLabel
<resourceLabel>
<key value="<key>" />
<value value="<value>" />
</resourceLabel>
If resourceType
has been manually set, then resourceLabel
is used to specify extra labels as shown in the Monitoring Resource Types list.
DisableResourceTypeDetection
<disableResourceTypeDetection value="true">
Defaults to false
. Setting this to true
disables automatic resourceType
setting based on platform detection.
Complete configuration example
So, for example, a complete configuration might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="CloudLogger" type="Google.Cloud.Logging.Log4Net.GoogleStackdriverAppender,Google.Cloud.Logging.Log4Net">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message" />
</layout>
<projectId value="myProject" />
<logId value="myLog" />
<withMetaData value="Location" />
<withMetaData value="ThreadName" />
</appender>
</log4net>
Configuration example: Translating Log4Net properties to Google Cloud Logging labels
If you wanted to transalate Log4Net properties to Google Cloud Logging labels, you can do as follows:
Create a log4net
configuration file (log4net.xml
):
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="CloudLogger" type="Google.Cloud.Logging.Log4Net.GoogleStackdriverAppender,Google.Cloud.Logging.Log4Net">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message" />
</layout>
<projectId value="PROJECT_ID" />
<logId value="LOG_ID" />
<usePatternWithinCustomLabels value="true"/>
<customLabel>
<key value="label_from_property" />
<value value="%property{property_for_label}" />
</customLabel>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="CloudLogger" />
</root>
</log4net>
Edit the file replacing PROJECT_ID
with your Google Cloud Project
ID, and LOG_ID
with an identifier for your application.
Use this file to configure log4net
and then set Log4Net properties per scopes and log as normal.
If a property referenced in a custom label doesn't have a value for a given entry, the value will be
(null)
.
// Configure log4net to use Google Cloud Logging from the XML configuration file.
XmlConfigurator.Configure(LogManager.GetRepository(GetType().Assembly), new FileInfo("log4net.xml"));
// Retrieve a logger for this context.
ILog log = LogManager.GetLogger(typeof(Program));
// Set a property using one of the GlobaContext, ThreadContext or LogicalThreadContext classes.
LogicalThreadContext.Properties["property_for_label"] = "my label value";
// Log some information. This log entry will be sent to Google Cloud Logging with a label
// {"label_from_property", "my label value"}. The label key "label_from_property" is the one
// specified in configuration for a custom label that gets its value from the "property_for_label"
// property.
log.Info("An exciting log entry!");
// Flush buffered log entries before program exit.
// This is required because log entries are buffered locally before being sent to Google Cloud Logging.
// LogManager.Flush() only works in the full .NET framework (not in .NET Core):
bool flushCompleted = LogManager.Flush(10_000);
// On .NET Core, the specific repository needs to be flushed:
bool repositoryFlushCompleted = ((IFlushable)LogManager.GetRepository(GetType().Assembly)).Flush(10_000);
If executing on Google App Engine (GAE),
Google Kubernetes Engine (GKE),
or Google Compute Engine (GCE),
then the <projectId value="PROJECT_ID" />
configuration setting can be omitted; it will be auto-detected from the platform at run-time.
Local queuing configuration
All log entries are queued locally before being sent to Google Cloud Logging. Only in-memory queuing is currently supported. Persistent on-disk queuing is not currently implemented.
The following in-memory options are available:
<MaxMemorySize value="<maximum number of bytes to queue>"/>
Restricts the amount of memory used for local queuing. If memory is exceeded then the oldest log entries will be discarded. A best-effort attempt will be made to log a warning to Google Cloud Logging that some log messages have been discarded. A value of 0 means there is no byte limit.
<MaxMemoryCount value="<maximum count of log messages to queue"/>
The maximum count of log messages that be ever be concurrently locally queued. If this maximum count is exceeded then the oldest log entries will be discarded. A best-effort attempt will be made to log a warning to Google Cloud Logging that some log messages have been discarded. A value of 0 means there is no count limit.
If MaxMemorySize and MaxMemoryCount are both specified, then messages will be discarded when the first limit is reached.
The default setting is to limit count to 1,000; with no limit on bytes.
Providing a JSON payload
Log entries in Google Cloud Logging either have a text payload or a
JSON payload. By default, the
Google.Cloud.Logging.Log4Net.GoogleStackdriverAppender
uses a text
payload. However, you can implement the
IJsonLayout interface and configure
the appender to use that. The JSON payload is expressed as a
Protocol Buffers Struct
message. If the IJsonLayout.Format
method returns null from your layout, the appender will create a
text payload as normal.
For example, here's a sample JSON layout implementation that demonstrates creating a payload with a mixture of values from execution time (the event message and exception) and from configuration (the "SampleConfiguration" value).
public class SampleJsonLayout : IJsonLayout, IOptionHandler
{
public Struct _template;
public int SampleConfigurationValue { get; set; }
public void ActivateOptions()
{
_template = new Struct();
_template.Fields["SampleConfiguration"] = Value.ForNumber(SampleConfigurationValue);
}
public Struct Format(LoggingEvent loggingEvent)
{
Struct ret = _template.Clone();
ret.Fields["Message"] = Value.ForString(loggingEvent.RenderedMessage);
if (loggingEvent.ExceptionObject != null)
{
ret.Fields["Exception"] = Value.ForString(loggingEvent.ExceptionObject.Message);
}
return ret;
}
}
You would then configure the appender in XML like this:
<appender name="CloudLogger" type="Google.Cloud.Logging.Log4Net.GoogleStackdriverAppender,Google.Cloud.Logging.Log4Net">
<!-- The IJsonLayout implementation used to create JSON payloads -->
<jsonLayout type="YourNamespace.SampleJsonLayout,YourAssemblyName">
<sampleConfigurationValue value="10" />
</jsonLayout>
<!--
- Other configuration as normal. The textlayout is used if the
- JSON layout returns null.
-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message" />
</layout>
<projectId value="PROJECT_ID" />
<logId value="LOG_ID" />
</appender>
Feedback...
Please file GitHub issues for bugs or questions.