commit cb84fe3411e78a352e0a812304c478664f27d866 Author: Liam Pietralla Date: Mon Feb 16 17:36:47 2026 +1100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bbb800 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json + +# Rider +.idea/ + +# Visual Studio +.vs/ + +# Fleet +.fleet/ + +# Code Rush +.cr/ + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Node.js build artifacts +node_modules/ +package-lock.json +package.json diff --git a/LoggingApi.csproj b/LoggingApi.csproj new file mode 100644 index 0000000..8467203 --- /dev/null +++ b/LoggingApi.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + + + + + + + + diff --git a/LoggingApi.sln b/LoggingApi.sln new file mode 100644 index 0000000..c41c245 --- /dev/null +++ b/LoggingApi.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggingApi", "LoggingApi.csproj", "{06EE0C43-C200-C32E-7D42-5A6E5B7F26C8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {06EE0C43-C200-C32E-7D42-5A6E5B7F26C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06EE0C43-C200-C32E-7D42-5A6E5B7F26C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06EE0C43-C200-C32E-7D42-5A6E5B7F26C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06EE0C43-C200-C32E-7D42-5A6E5B7F26C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A4C3FE80-F377-4609-9507-B69321D4CB00} + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..7958b5c --- /dev/null +++ b/Program.cs @@ -0,0 +1,33 @@ +using OpenTelemetry.Logs; + +var builder = WebApplication.CreateBuilder(args); + +var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + + if (useOtlpExporter) + { + logging.AddOtlpExporter(); + } + else + { + Console.WriteLine("OTEL_EXPORTER_OTLP_ENDPOINT is not set. Skipping OTLP exporter configuration."); + } +}); + +var app = builder.Build(); + +app.MapGet("/", () => "Hello World!"); + +app.MapGet("/log", (ILogger logger) => +{ + if (logger.IsEnabled(LogLevel.Information)) + logger.LogInformation("Logging a message at {time:HH:mm:ss} on {machine}", DateTime.Now, Environment.MachineName); + return Results.Ok(); +}); + +app.Run(); diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..9e8fac9 --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5171", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7247;http://localhost:5171", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..89e50ae --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_SERVICE_NAME": "LoggingApi" +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..6e150e8 --- /dev/null +++ b/compose.yml @@ -0,0 +1,51 @@ +services: + alloy: + image: grafana/alloy:v1.13.1 + ports: + - 12345:12345 + - 4317:4317 + - 4318:4318 + volumes: + - ./config.alloy:/etc/alloy/config.alloy + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + + loki: + image: grafana/loki:3.5.10 + volumes: + - ./loki-config.yaml:/etc/loki/local-config.yaml + - loki-data:/loki + command: -config.file=/etc/loki/local-config.yaml + + grafana: + image: grafana/grafana:12.3 + environment: + - GF_FEATURE_TOGGLES_ENABLE=grafanaManagedRecordingRules + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + ports: + - 3001:3000/tcp + entrypoint: + - sh + - -euc + - | + mkdir -p /etc/grafana/provisioning/datasources + cat < /etc/grafana/provisioning/datasources/ds.yaml + apiVersion: 1 + datasources: + - name: Loki + type: loki + access: proxy + orgId: 1 + url: 'http://loki:3100' + basicAuth: false + isDefault: true + version: 1 + editable: true + EOF + /run.sh + +volumes: + loki-data: \ No newline at end of file diff --git a/config.alloy b/config.alloy new file mode 100644 index 0000000..73bcb9f --- /dev/null +++ b/config.alloy @@ -0,0 +1,24 @@ +otelcol.receiver.otlp "default" { + http { + endpoint = "0.0.0.0:4318" + } + grpc { + endpoint = "0.0.0.0:4317" + } + + output { + logs = [otelcol.processor.batch.default.input] + } +} + +otelcol.processor.batch "default" { + output { + logs = [otelcol.exporter.otlphttp.loki.input] + } +} + +otelcol.exporter.otlphttp "loki" { + client { + endpoint = "http://loki:3100/otlp" + } +} diff --git a/loki-config.yaml b/loki-config.yaml new file mode 100644 index 0000000..49de033 --- /dev/null +++ b/loki-config.yaml @@ -0,0 +1,35 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + +common: + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /loki + +schema_config: + configs: + - from: 2020-05-15 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + filesystem: + directory: /loki/chunks + +compactor: + working_directory: /loki/compactor + retention_enabled: true + compaction_interval: 24h + delete_request_store: filesystem + +limits_config: + retention_period: 720h # 30 days