I'm working on a .NET desktop application and need to send logs to AWS CloudWatch. My team created a NuGet package to standardize Serilog logging across all our projects.
Our setup works fine for ECS-hosted applications, but for desktop applications, we're looking for the best way to send logs to CloudWatch.
Current Implementation
public static Serilog.Core.Logger CreateEndpointLoggerWithCloudWatch(
string applicationName, string logGroup, string region, LogOutput output = LogOutput.None)
{
AWSLoggerConfig awsConfiguration = new(logGroup) { Region = region };
LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
.WriteTo.AWSSeriLog(awsConfiguration, textFormatter: new ExpressionTemplate(JsonLogTemplate));
return loggerConfiguration.CreateLogger();
}
This works fine in ECS, but for desktop applications, we face challenges such as:
- Ensuring logs are delivered reliably (especially with intermittent internet connectivity).
- Managing authentication securely without embedding AWS credentials.
- Handling batching and retries efficiently to prevent excessive API calls.
My questions
- What is the best approach to send logs to AWS CloudWatch from a desktop application?
- Is there a recommended way to authenticate without hardcoding AWS credentials?
- Should we use a background service to batch logs before sending them?
Set credentials in .aws file
The first solution we tried was to set AWS credentials in the .aws file. While this approach works well, I am not fully convinced about its reliability and security.
Create cognito identity pool
Another approach was to create a Cognito Identity Pool for guest users and define an IAM role that allows the application to send logs to CloudWatch.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudWatchLogsFullAccess",
"Effect": "Allow",
"Action": [
"logs:*",
"cloudwatch:GenerateQuery"
],
"Resource": "*"
}
]
}
the trust relationship
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws:aud": "eu-xxx-1:xxx-xxx-xxx"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws:amr": "unauthenticated"
}
}
}
]
}
To authenticate our application, we need to pass credentials to the AWSLoggerConfig
object using the Cognito Identity Pool ID:
public static Serilog.Core.Logger CreateEndpointLoggerWithCloudWatch(
string applicationName, string logGroup, string region, LogOutput output = LogOutput.None, string identityPoolId)
{
AWSLoggerConfig awsConfiguration = new(logGroup)
{
Region = region,
Credentials = new CognitoAWSCredentials(identityPoolId, RegionEndpoint.GetBySystemName(region));
};
LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
.WriteTo.AWSSeriLog(awsConfiguration, textFormatter: new ExpressionTemplate(JsonLogTemplate));
return loggerConfiguration.CreateLogger();
}
This solution works and allows us to log unauthenticated actions. However, I am concerned about potential security risks associated with this approach.
Use a background service
Another option would be to implement a background service that acts as a gateway between our application and CloudWatch. However, I am not convinced that this is the best approach, as it adds an additional layer of complexity and potential failure points.