Logging, Tracing, and OpenTelemetry
Logging with ILogger
and Scopes
This project uses Microsoft's ILogger
interface for logging. It provides a standardized and extensible way to capture events within the application.
Using scopes enables hierarchical organization of log messages and allows contextual information to be attached to each entry.
ILogger Basics
The ILogger
interface is part of Microsoft.Extensions.Logging
and provides methods to log messages at various severity levels (e.g., Information
, Warning
, Error
).
Logging can be enabled using either WebApplication.CreateBuilder
, Host.CreateDefaultBuilder
, or the AddLogging
extension method on the IServiceCollection
.
You can log from your code by injecting ILogger<MyEntityController>
(or a similar type) into your component.
Using Scopes
Scopes define a logical boundary in which all log entries are automatically enriched with contextual metadata. This is especially useful for correlating logs related to a specific request or operation.
By default, the ResourceWatcher starts a new scope for every watch event it processes. Each scope includes:
EventType
: Type of the received watch event (Added
,Modified
,Deleted
,Error
,Bookmark
)Kind
: Custom Resource Definition (CRD) kindNamespace
: CRD namespaceName
: CRD nameResourceVersion
: CRD resource version
You can create additional scopes in your code using logger.BeginScope(state)
, where state
can be either a string or a custom object.
To include scopes in the logging output, they must be explicitly enabled either via configuration or code:
appsettings.json
:
"Logging": {
"Console": {
"FormatterName": "Simple",
"FormatterOptions": {
"IncludeScopes": true
}
},
"LogLevel": {
"Default": "Information",
"KubeOps": "Trace"
}
}
Programmatic configuration:
builder
.ConfigureLogging((hostBuilderContext, loggingBuilder) =>
{
loggingBuilder
.AddSimpleConsole(options => options.IncludeScopes = true);
});
To enable scopes with OpenTelemetry, configure it as follows:
"OpenTelemetry": {
"IncludeScopes": true,
"ParseStateValues": true,
"IncludeFormattedMessage": true
}
The scope state must be an IReadOnlyDictionary<string, object?>
to ensure correct serialization and inclusion in log entries.
Tracing with System.Diagnostics
and ActivitySource
For distributed tracing, this project uses System.Diagnostics
in combination with ActivitySource
.
Activities can be started using ActivitySource.StartActivity
.
The operator registers an ActivitySource
instance with the operator name in the dependency injection (DI) container. To use a custom ActivitySource
, simply register your own, the DI will provide the last registered instance when requested.
You can configure the operator name (and thus the ActivitySource
name) via OperatorSettings
:
const string OperatorName = "my-operator";
builder
.Services
.AddKubernetesOperator(settings => settings.Name = OperatorName);
If you're using OpenTelemetry, tracing must be explicitly configured in code. Make sure to add the same source name used when creating the ActivitySource
.
Also, create a ResourceBuilder
to name your service properly in trace output:
builder.Services
.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
tracerProviderBuilder
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: OperatorName, serviceVersion: "1.0.0"))
.AddSource(OperatorName));
OpenTelemetry Configuration for Azure Logging
To use OpenTelemetry with Azure, it is recommended to adopt the Azure Monitor OpenTelemetry Distro. You can enable it via code:
builder.Services
.AddOpenTelemetry()
.UseAzureMonitor();
Full Example Configuration in Program.cs
(or Startup.cs
)
A complete setup with logging, tracing, and OpenTelemetry might look like this:
appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"KubeOps": "Trace"
},
"Console": {
"FormatterName": "Simple",
"FormatterOptions": {
"IncludeScopes": true,
"SingleLine": true
}
},
"OpenTelemetry": {
"IncludeScopes": true,
"ParseStateValues": true,
"IncludeFormattedMessage": true
}
}
}
Program.cs
:
const string OperatorName = "my-platform-operator";
var builder = WebApplication.CreateBuilder(args);
builder
.Services
.AddKubernetesOperator(settings => settings.Name = OperatorName)
.RegisterComponents();
builder
.Services
.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
tracerProviderBuilder
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: OperatorName, serviceVersion: "1.0.0"))
.AddSource(OperatorName))
.UseAzureMonitor();