7. Choosing correct type of instrumentation
Log
• What
happened?
• Errors and
warnings and
state changes
• Important
state changes
worth
registering
• Always logged
Trace
• How did it
happen?
• Circumstantial
data for
following flow
• Available on
request
• Publish/
subscribe
model mostly
• High volume
Metric
• How much is
happening?
• Numerical
information of
events
• Suitable for
aggregation
and trends
Health
• How is it
doing?
• Information
indicating
health status
• Internally
determined by
component
Audit
• Who made it
happen?
• Data
regarding
actions
performed by
someone
• Always
• Security and
identity
related
• Proof for non-
repudiability
9. Logging
Persisting important events in a log
Built-in since .NET Core 1.0
Used internally by CoreFX libraries
Bootstrapping changed from .NET Core 3.0 forward
12. Log severity levels and categories
Hierarchical structure
Much like namespaces in C#, separated by dots
Microsoft
Microsoft.Hosting.Lifetime
Determined from T in ILogger<T>
Except when creating ILogger directly with LoggerFactory.CreateLogger
public enum LogLevel
{
Trace = 0,
Debug = 1,
Information = 2,
Warning = 3,
Error = 4,
Critical = 5,
None = 6
}
ILogger<LeaderboardController>
namespace LeaderboardWebApi.Controllers {
public class LeaderboardController : Controller { … }
}
"LeaderboardWebApi.Controllers.LeaderboardController"
13. Separate configuration
for specific providers
General configuration
for all providers
Log filters
Filter log messages
By provider and per log category based on severity
Can read filters from configuration (appsettings.json)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": { … }
"IncludeScopes": true
}
}
ILogger<T>
16. Semantic logging (aka structured logging)
Message templates
Strings with named placeholders for semantics
Avoid string interpolation in messages
Popular log providers supporting semantic logging
// Placeholders in template become queryable custom data
logger.LogInformation("Searching with {SearchLimit} results.", limit);
18. Scopes
Groups a set of logical operations
Requires setting options to include scope for provider
logging.AddConsole(options => options.IncludeScopes = true);
using (logger.BeginScope("Message attached to logs created in the using block"))
{
logger.LogInformation(LoggingEvents.NewHighScore, “New high score {score}", highScore);
var game = gameRepository.Find(gameId);
if (game == null)
{
logger.LogWarning(LoggingEvents.GameNotFound, "GetById({game}) not found", gameId);
return NotFound();
}
}
19. Logging guidance
Separation of concerns
“A twelve-factor app never concerns itself with routing or storage of its output stream."
Choose your log severity level carefully
You might get paged during the night
Use event IDs when possible
Log messages are not for UI
No need to localize your messages
When in doubt, be generous on logging
Tooling will help you filter and aggregate
20. Alternative logging solutions
Serilog
Popular open source logging framework
Many log providers available
Replaces built-in logging system
Can also log to .NET log providers
More functionality
Log appenders (aka enrichers) for additional data
Large set of providers
Serilog
22. Diagnostics Trace
High volume noise to follow flow
Not a log of things that happened
Publish/subscribe
Requires a listener to receive information
Information not persisted unless subscribed
System.Diagnostics namespace
Trace and TraceSource entrypoints
Needs compilation constant #TRACE
Uses activities under the cover
23. Tracing infrastructure
Trace
“Singleton” with static methods
Traditional tracing
TraceData
TraceEvent
TraceInformation
TraceTransfer
TraceError
TraceWarning
TraceInformation
Write(If)
WriteLine(If)
TraceSource
24. Activities
Ambient contextual data
Accessible from Activity.Current
Thread-safe and async aware
Used for correlating events
Even across network calls
New W3C tracing format
Application Insights uses activities
Parent/Child relationship
var activity = new Activity("SearchEngine.Run");
activity.SetStartTime(DateTime.Now);
activity.Start();
activity.Track...();
25. Available trace listeners
Namespace Class .NET version
System.Diagnostics DefaultTraceListener Core 1.0+
TextWriterTraceListener Core 1.0+
EventLogTraceListener Core 3.0
ConsoleTraceListener Core 3.0
DelimitedListTraceListener Core 1.0+
XmlWriterTraceListener Core 3.0
EventSchemaTraceListener .NET FX 3.5+
System.Diagnostics.Eventing EventProviderTraceListener .NET FX 3.5+
System.Web IisTraceListener .NET FX 2.0+
WebPageTraceListener .NET FX 2.0+
Microsoft.VisualBasic.Logging FileLogTraceListener .NET FX 2.0+
From .NET Framework 2.0 to .NET Core 3.0 to .NET 5
29. Health check publishers
Pushes out health
info periodically
Options
ASP
.NET Core application
DefaultHealthCheckService
HealthCheckPublisher
HostedService
IEnumerable<IHealthCheckPublisher>
services.AddHealthChecks()
.AddApplicationInsightsPublisher()
.AddPrometheusGatewayPublisher(
"http://pushgateway:9091/metrics",
"pushgateway") IHealthCheckPublisher
30. Probing containers to check for availability and health
Readiness and liveness
Kubernetes node
Kubernetes node
Kubernetes nodes
Containers
Readiness
Liveliness
31. Implementing readiness and liveliness
1. Add health checks with tags
2. Register multiple endpoints
with filter using
Options predicate
/api/v1/…
/health
/health/ready
/health/lively
app.UseHealthChecks("/health/ready",
new HealthCheckOptions() {
Predicate = reg => reg.Tags.Contains("ready")
});
services.AddHealthChecks()
.AddCheck<CircuitBreakerHealthCheck>(
"circuitbreakers",
tags: new string[] { "ready" });
app.UseHealthChecks("/health/lively",
new HealthCheckOptions() {
Predicate = _ => true
});