Prerequisites
Option 1: Using HTTP Client
Create a new .NET project
Create a new .NET project. In your terminal, go to the directory where you want to create your project. Run the following command to create a new console app named AxiomLogs
.
dotnet new console -n AxiomLogs
Install packages
Install the packages for your project. Use the Microsoft.AspNet.WebApi.Client
package to make HTTP requests to the Axiom API. Run the following command to install the package:
dotnet add package Microsoft.AspNet.WebApi.Client
Create a class to handle logging to Axiom. Create a new file named AxiomLogger.cs
in your project directory with the following content:
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public static class AxiomLogger
{
public static async Task LogToAxiom(string message, string logLevel)
{
// Create an instance of HttpClient to make HTTP requests
var client = new HttpClient();
// Specify the Axiom dataset name and construct the API endpoint URL
var datasetName = "DATASET-NAME"; // Replace with your actual dataset name
var axiomUri = $"https://api.axiom.co/v1/datasets/{datasetName}/ingest";
// Replace with your Axiom API token
var apiToken = "API-TOKEN"; // Ensure your API token is correct
// Create an array of log entries, including the timestamp, message, and log level
var logEntries = new[]
{
new
{
timestamp = DateTime.UtcNow.ToString("o"),
message = message,
level = logLevel
}
};
// Serialize the log entries to JSON format using System.Text.Json.JsonSerializer
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(logEntries), Encoding.UTF8, "application/json");
// Set the authorization header with the Axiom API token
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiToken);
// Make a POST request to the Axiom API endpoint with the serialized log entries
var response = await client.PostAsync(axiomUri, content);
// Check the response status code
if (!response.IsSuccessStatusCode)
{
// If the response is not successful, print the error details
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Failed to send log: {response.StatusCode}\n{responseBody}");
}
else
{
// If the response is successful, print "Log sent successfully."
Console.WriteLine("Log sent successfully.");
}
}
}
- Replace
API_TOKEN
with the Axiom API token you have generated. For added security, store the API token in an environment variable.
- Replace
DATASET_NAME
with the name of the Axiom dataset where you want to send data.
Configure the main program
Now that the Axiom logger is in place, update the main program so it can be used. Open the Program.cs
file and replace its contents with the following code:
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// Log the application startup event with an "INFO" log level
await AxiomLogger.LogToAxiom("Application started", "INFO");
// Call the SimulateOperations method to simulate various application operations
await SimulateOperations();
// Log the .NET runtime version information with an "INFO" log level
await AxiomLogger.LogToAxiom($"CLR version: {Environment.Version}", "INFO");
// Log the application shutdown event with an "INFO" log level
await AxiomLogger.LogToAxiom("Application shutting down", "INFO");
}
static async Task SimulateOperations()
{
// Log the start of operations with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Starting operations", "DEBUG");
// Log the database connection event with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Connecting to database", "DEBUG");
await Task.Delay(500); // Simulated delay
// Log the successful database connection with an "INFO" log level
await AxiomLogger.LogToAxiom("Connected to database successfully", "INFO");
// Log the user data retrieval event with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Retrieving user data", "DEBUG");
await Task.Delay(1000);
// Log the number of retrieved user records with an "INFO" log level
await AxiomLogger.LogToAxiom("Retrieved 100 user records", "INFO");
// Log the user preference update event with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Updating user preferences", "DEBUG");
await Task.Delay(800);
// Log the successful user preference update with an "INFO" log level
await AxiomLogger.LogToAxiom("Updated user preferences successfully", "INFO");
try
{
// Log the payment processing event with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Processing payments", "DEBUG");
await Task.Delay(1500);
// Intentionally throw an exception to demonstrate error logging
throw new Exception("Payment gateway unavailable");
}
catch (Exception ex)
{
// Log the payment processing failure with an "ERROR" log level
await AxiomLogger.LogToAxiom($"Payment processing failed: {ex.Message}", "ERROR");
}
// Log the email notification sending event with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Sending email notifications", "DEBUG");
await Task.Delay(1200);
// Log the number of sent email notifications with an "INFO" log level
await AxiomLogger.LogToAxiom("Sent 50 email notifications", "INFO");
// Log the high memory usage detection with a "WARN" log level
await AxiomLogger.LogToAxiom("Detected high memory usage", "WARN");
await Task.Delay(500);
// Log the memory usage normalization with an "INFO" log level
await AxiomLogger.LogToAxiom("Memory usage normalized", "INFO");
// Log the completion of operations with a "DEBUG" log level
await AxiomLogger.LogToAxiom("Operations completed", "DEBUG");
}
}
This code simulates various app operations and logs messages at different levels (DEBUG, INFO, WARN, ERROR) to Axiom.
Project file configuration
Ensure your axiomlogs.csproj
file is configured with the package reference. The file should look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
</ItemGroup>
</Project>
Build and run the app
To build and run the app, go to the project directory in your terminal and run the following command:
This command builds the project and runs the app. You see the log messages being sent to Axiom, and the console displays Log sent successfully.
for each log entry.
Option 2: Using Serilog
Install Serilog Packages
Add Serilog and the necessary extensions to your project. You need the Serilog
, Serilog.Sinks.Http
, Serilog.Formatting.Elasticsearch
and Serilog.Formatting.Json
packages.
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.Http
dotnet add package Serilog.Formatting.Elasticsearch
dotnet add package Microsoft.Extensions.Configuration
In your Program.cs
or a startup configuration file, set up Serilog to use the HTTP sink. Configure the sink to point to the Axiom ingestion API endpoint.
using Serilog;
using Serilog.Formatting.Elasticsearch;
using Serilog.Sinks.Http;
using System.Net.Http;
using System.Net.Http.Headers;
using System.IO;
using Microsoft.Extensions.Configuration;
public class AxiomConfig
{
public const string DatasetName = "DATASET_NAME";
public const string ApiToken = "API_TOKEN";
public const string ApiUrl = "https://api.axiom.co/v1/datasets";
}
public class AxiomHttpClient : IHttpClient
{
private readonly HttpClient _httpClient;
public AxiomHttpClient()
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", AxiomConfig.ApiToken);
}
public void Configure(IConfiguration configuration)
{
}
public async Task<HttpResponseMessage> PostAsync(string requestUri, Stream contentStream, CancellationToken cancellationToken = default)
{
var content = new StreamContent(contentStream);
content.Headers.Add("Content-Type", "application/json");
return await _httpClient.PostAsync(requestUri, content, cancellationToken).ConfigureAwait(false);
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
public class Program
{
public static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Http(
requestUri: $"{AxiomConfig.ApiUrl}/{AxiomConfig.DatasetName}/ingest",
queueLimitBytes: null,
textFormatter: new ElasticsearchJsonFormatter(renderMessageTemplate: false, inlineFields: true),
httpClient: new AxiomHttpClient()
)
.CreateLogger();
try
{
Log.Information("Application started on .NET 8");
await SimulateOperations();
Log.Information($"Runtime version: {Environment.Version}");
Log.Information("Application shutting down");
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
await Log.CloseAndFlushAsync();
}
}
static async Task SimulateOperations()
{
Log.Debug("Starting operations");
Log.Debug("Connecting to database");
await Task.Delay(500);
Log.Information("Connected to database successfully");
Log.Debug("Retrieving user data");
await Task.Delay(1000);
Log.Information("Retrieved 100 user records");
Log.Debug("Updating user preferences");
await Task.Delay(800);
Log.Information("Updated user preferences successfully");
try
{
Log.Debug("Processing payments");
await Task.Delay(1500);
throw new Exception("Payment gateway unavailable");
}
catch (Exception ex)
{
Log.Error(ex, "Payment processing failed: {ErrorMessage}", ex.Message);
}
Log.Debug("Sending email notifications");
await Task.Delay(1200);
Log.Information("Sent 50 email notifications");
Log.Warning("Detected high memory usage: {UsagePercentage}%", 85);
await Task.Delay(500);
Log.Information("Memory usage normalized");
Log.Debug("Operations completed");
}
}
- Replace
API_TOKEN
with the Axiom API token you have generated. For added security, store the API token in an environment variable.
- Replace
DATASET_NAME
with the name of the Axiom dataset where you want to send data.
Project file configuration
Ensure your axiomlogs.csproj
file is configured with the package references. The file should look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>SerilogApp</AssemblyName>
<RootNamespace>SerilogApp</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0" />
</ItemGroup>
</Project>
Build and run the app
To build and run the app, go to the project directory in your terminal and run the following commands:
This command builds the project and runs the app. You see the log messages being sent to Axiom.
Option 3: Using NLog
Install NLog Packages
You need NLog and potentially an extension for HTTP targets.
dotnet add package NLog
dotnet add package NLog.Web.AspNetCore
dotnet add package NLog.Targets.Http
Set up NLog by creating an NLog.config
file or configuring it programmatically. Here is an example configuration for NLog
using an HTTP target:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="NLog.Targets.Http" />
</extensions>
<targets>
<target xsi:type="BufferingWrapper"
name="allLogs"
flushTimeout="5000">
<target xsi:type="HTTP"
name="axiom"
url="https://api.axiom.co/v1/datasets/DATASET_NAME/ingest"
HttpHeaders="Authorization: Bearer API_TOKEN"
contentType="application/json">
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="timestamp" layout="${date:universalTime=true:format=o}" />
<attribute name="level" layout="${level:lowercase=true}" />
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=toString}"
encode="false" />
</layout>
</target>
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="allLogs" />
</rules>
</nlog>
- Replace
API_TOKEN
with the Axiom API token you have generated. For added security, store the API token in an environment variable.
- Replace
DATASET_NAME
with the name of the Axiom dataset where you want to send data.
Configure the main program
Update the main program to use NLog
. In your Program.cs
file:
using NLog;
using NLog.Web;
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
class Program
{
static async Task Main(string[] args)
{
logger.Info("Application started");
await SimulateOperations();
logger.Info($"CLR version: {Environment.Version}");
logger.Info("Application shutting down");
}
static async Task SimulateOperations()
{
logger.Debug("Starting operations");
logger.Debug("Connecting to database");
await Task.Delay(500); // Simulated delay
logger.Info("Connected to database successfully");
logger.Debug("Retrieving user data");
await Task.Delay(1000);
logger.Info("Retrieved 100 user records");
logger.Debug("Updating user preferences");
await Task.Delay(800);
logger.Info("Updated user preferences successfully");
try
{
logger.Debug("Processing payments");
await Task.Delay(1500);
throw new Exception("Payment gateway unavailable");
}
catch (Exception ex)
{
logger.Error($"Payment processing failed: {ex.Message}");
}
logger.Debug("Sending email notifications");
await Task.Delay(1200);
logger.Info("Sent 50 email notifications");
logger.Warn("Detected high memory usage");
await Task.Delay(500);
logger.Info("Memory usage normalized");
logger.Debug("Operations completed");
}
}
Project file configuration
Ensure your axiomlogs.csproj
file is configured with the package references. The file should look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="4.7.12" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.3" />
<PackageReference Include="NLog.Targets.Http" Version="1.0.4" />
</ItemGroup>
</Project>
Build and run the app
To build and run the app, go to the project directory in your terminal and run the following commands:
This command builds the project and runs the app. You should see the log messages being sent to Axiom.
Best practices for logging
To make your logging more effective, consider the following best practices:
- Include relevant information such as user IDs, request details, and system state in your log messages to provide context when investigating issues.
- Use different log levels (DEBUG, INFO, WARN, ERROR) to categorize the severity and importance of log messages. This allows you to filter and analyze logs more effectively
- Use structured logging formats like JSON to make it easier to parse and analyze log data
Conclusion
This guide covers the steps to send logs from a C# .NET app to Axiom. By following these instructions and adhering to logging best practices, you can effectively monitor your app, diagnose issues, and gain valuable insights into its behavior.