Skip to content

Commit 5400099

Browse files
Change AccountService from Go to DotNet (auto) (#1538)
* Change AccountService from go to dotnet (auto) * fix path * fix folder name * dockerfile and other fixes * add copyright * fix encoding and cleanup * Cleanup dockerfile * Update OTel Auto * fix kafka processing issues and otel export * remove eof * Update changelog * Use default CancellationDelayMaxMs * update packages * fix merge failure * Fix tracetest 'accountingservice' is not part of the trace anymore --------- Co-authored-by: Juliano Costa <[email protected]> Co-authored-by: Juliano Costa <[email protected]>
1 parent 918e86a commit 5400099

File tree

17 files changed

+263
-519
lines changed

17 files changed

+263
-519
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ the release.
77

88
## Unreleased
99

10+
* [accountingservice] convert from Go service to .NET service, uses
11+
OpenTelemetry .NET Automatic Instrumentation.
12+
([#1538](https://github.com/open-telemetry/opentelemetry-demo/pull/1538))
13+
* [frontend] fixed default flagd port for HTTPS connections
14+
([#1609](https://github.com/open-telemetry/opentelemetry-demo/pull/1609))
1015
* [cartservice] bump .NET package to 1.9.0 release
1116
([#1610](https://github.com/open-telemetry/opentelemetry-demo/pull/1610))
1217
* [Valkey] Replace Redis with Valkey
1318
([#1619](https://github.com/open-telemetry/opentelemetry-demo/pull/1619))
14-
* [frontend] fixed default flagd port for HTTPS connections
15-
([#1609](https://github.com/open-telemetry/opentelemetry-demo/pull/1609))
1619
* ([recommendation] updated flag name to match flagd configuration
1720
([#1634](https://github.com/open-telemetry/opentelemetry-demo/pull/1634))
1821

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ services:
2929
deploy:
3030
resources:
3131
limits:
32-
memory: 20M
32+
memory: 50M
3333
restart: unless-stopped
3434
environment:
3535
- KAFKA_SERVICE_ADDR
36-
- OTEL_EXPORTER_OTLP_ENDPOINT
36+
- OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_HTTP}
3737
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
3838
- OTEL_RESOURCE_ATTRIBUTES
3939
- OTEL_SERVICE_NAME=accountingservice
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Confluent.Kafka" Version="2.4.0" />
13+
<PackageReference Include="Google.Protobuf" Version="3.27.1" />
14+
<PackageReference Include="Grpc.Tools" Version="2.64.0">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17+
</PackageReference>
18+
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
19+
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
20+
<PackageReference Include="OpenTelemetry.AutoInstrumentation" Version="1.7.0" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<!-- GrpcServices is 'none' so that we do not need to depend on the grpc nuget package, and we only need protobuf support. -->
25+
<Protobuf Include="proto\demo.proto" GrpcServices="none" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<Folder Include="proto\" />
30+
</ItemGroup>
31+
32+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34701.34
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccountingService", "AccountingService.csproj", "{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {6340CDDC-E917-4532-A056-5526E0A7BDDA}
24+
EndGlobalSection
25+
EndGlobal

src/accountingservice/Consumer.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Confluent.Kafka;
5+
using Microsoft.Extensions.Logging;
6+
using Oteldemo;
7+
8+
namespace AccountingService;
9+
10+
internal class Consumer : IDisposable
11+
{
12+
private const string TopicName = "orders";
13+
14+
private ILogger _logger;
15+
private IConsumer<string, byte[]> _consumer;
16+
private bool _isListening;
17+
18+
public Consumer(ILogger<Consumer> logger)
19+
{
20+
_logger = logger;
21+
22+
var servers = Environment.GetEnvironmentVariable("KAFKA_SERVICE_ADDR")
23+
?? throw new ArgumentNullException("KAFKA_SERVICE_ADDR");
24+
25+
_consumer = BuildConsumer(servers);
26+
_consumer.Subscribe(TopicName);
27+
28+
_logger.LogInformation($"Connecting to Kafka: {servers}");
29+
}
30+
31+
public void StartListening()
32+
{
33+
_isListening = true;
34+
35+
try
36+
{
37+
while (_isListening)
38+
{
39+
try
40+
{
41+
var consumeResult = _consumer.Consume();
42+
43+
ProcessMessage(consumeResult.Message);
44+
}
45+
catch (ConsumeException e)
46+
{
47+
_logger.LogError(e, "Consume error: {0}", e.Error.Reason);
48+
}
49+
}
50+
}
51+
catch (OperationCanceledException)
52+
{
53+
_logger.LogInformation("Closing consumer");
54+
55+
_consumer.Close();
56+
}
57+
}
58+
59+
private void ProcessMessage(Message<string, byte[]> message)
60+
{
61+
try
62+
{
63+
var order = OrderResult.Parser.ParseFrom(message.Value);
64+
65+
Log.OrderReceivedMessage(_logger, order);
66+
}
67+
catch (Exception ex)
68+
{
69+
_logger.LogError(ex, "Order parsing failed:");
70+
}
71+
}
72+
73+
private IConsumer<string, byte[]> BuildConsumer(string servers)
74+
{
75+
var conf = new ConsumerConfig
76+
{
77+
GroupId = $"accountingservice",
78+
BootstrapServers = servers,
79+
// https://github.com/confluentinc/confluent-kafka-dotnet/tree/07de95ed647af80a0db39ce6a8891a630423b952#basic-consumer-example
80+
AutoOffsetReset = AutoOffsetReset.Earliest,
81+
EnableAutoCommit = true
82+
};
83+
84+
return new ConsumerBuilder<string, byte[]>(conf)
85+
.Build();
86+
}
87+
88+
public void Dispose()
89+
{
90+
_isListening = false;
91+
_consumer?.Dispose();
92+
}
93+
}

src/accountingservice/Dockerfile

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
# Copyright The OpenTelemetry Authors
22
# SPDX-License-Identifier: Apache-2.0
33

4-
5-
FROM golang:1.22-alpine AS builder
6-
7-
WORKDIR /usr/src/app
8-
9-
RUN apk update \
10-
&& apk add --no-cache make protobuf-dev
11-
12-
RUN --mount=type=cache,target=/go/pkg/mod/ \
13-
--mount=type=bind,source=./src/accountingservice/go.sum,target=go.sum \
14-
--mount=type=bind,source=./src/accountingservice/go.mod,target=go.mod \
15-
--mount=type=bind,source=./src/accountingservice/tools.go,target=tools.go \
16-
go mod download \
17-
&& go list -e -f '{{range .Imports}}{{.}} {{end}}' tools.go | CGO_ENABLED=0 xargs go install -mod=readonly
18-
19-
RUN --mount=type=cache,target=/go/pkg/mod/ \
20-
--mount=type=cache,target=/root/.cache/go-build \
21-
--mount=type=bind,rw,source=./src/accountingservice,target=. \
22-
--mount=type=bind,rw,source=./pb,target=./pb \
23-
protoc -I ./pb ./pb/demo.proto --go_out=./ --go-grpc_out=./ \
24-
&& go build -ldflags "-s -w" -o /go/bin/accountingservice/ ./
25-
26-
FROM alpine
27-
28-
WORKDIR /usr/src/app/
29-
30-
COPY --from=builder /go/bin/accountingservice/ ./
31-
32-
ENTRYPOINT [ "./accountingservice" ]
4+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
5+
USER app
6+
WORKDIR /app
7+
8+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
9+
ARG BUILD_CONFIGURATION=Release
10+
WORKDIR /src
11+
COPY ["/src/accountingservice/", "AccountingService/"]
12+
COPY ["/pb/demo.proto", "AccountingService/proto/"]
13+
RUN dotnet restore "./AccountingService/AccountingService.csproj"
14+
WORKDIR "/src/AccountingService"
15+
16+
RUN dotnet build "./AccountingService.csproj" -c $BUILD_CONFIGURATION -o /app/build
17+
18+
FROM build AS publish
19+
ARG BUILD_CONFIGURATION=Release
20+
RUN dotnet publish "./AccountingService.csproj" --use-current-runtime -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
21+
22+
FROM base AS final
23+
WORKDIR /app
24+
COPY --from=publish /app/publish .
25+
26+
USER root
27+
RUN mkdir -p "/var/log/opentelemetry/dotnet"
28+
RUN chown app "/var/log/opentelemetry/dotnet"
29+
USER app
30+
31+
ENTRYPOINT ["./instrument.sh", "dotnet", "AccountingService.dll"]

src/accountingservice/Helpers.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System.Collections;
5+
6+
namespace AccountingService
7+
{
8+
internal static class Helpers
9+
{
10+
private static List<string> RelevantPrefixes = ["DOTNET_", "CORECLR_", "OTEL_", "KAFKA_"];
11+
12+
public static IEnumerable<DictionaryEntry> FilterRelevant(this IDictionary envs)
13+
{
14+
foreach (DictionaryEntry env in envs)
15+
{
16+
foreach (var prefix in RelevantPrefixes)
17+
{
18+
if (env.Key.ToString()?.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase) ?? false)
19+
{
20+
yield return env;
21+
}
22+
}
23+
}
24+
}
25+
26+
public static void OutputInOrder(this IEnumerable<DictionaryEntry> envs)
27+
{
28+
foreach (var env in envs.OrderBy(x => x.Key))
29+
{
30+
Console.WriteLine(env);
31+
}
32+
}
33+
}
34+
}

src/accountingservice/Log.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Microsoft.Extensions.Logging;
5+
using Oteldemo;
6+
7+
namespace AccountingService
8+
{
9+
internal static partial class Log
10+
{
11+
[LoggerMessage(
12+
Level = LogLevel.Information,
13+
Message = "Order details: {@OrderResult}.")]
14+
public static partial void OrderReceivedMessage(ILogger logger, OrderResult orderResult);
15+
}
16+
}

src/accountingservice/Program.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using AccountingService;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Hosting;
7+
8+
Console.WriteLine("Accounting service started");
9+
10+
Environment.GetEnvironmentVariables()
11+
.FilterRelevant()
12+
.OutputInOrder();
13+
14+
var host = Host.CreateDefaultBuilder(args)
15+
.ConfigureServices(services =>
16+
{
17+
services.AddSingleton<Consumer>();
18+
})
19+
.Build();
20+
21+
var consumer = host.Services.GetRequiredService<Consumer>();
22+
consumer.StartListening();
23+
24+
host.Run();

src/accountingservice/README.md

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ This service consumes new orders from a Kafka topic.
77
To build the service binary, run:
88

99
```sh
10-
go build -o /go/bin/accountingservice/
10+
cp pb/demo.proto src/accoutingservice/proto/demo.proto # root context
11+
dotnet build # accounting service context
1112
```
1213

1314
## Docker Build
@@ -18,22 +19,10 @@ From the root directory, run:
1819
docker compose build accountingservice
1920
```
2021

21-
## Regenerate protos
22-
23-
> [!NOTE]
24-
> [`protoc`](https://grpc.io/docs/protoc-installation/) is required.
25-
26-
To regenerate gRPC code run:
27-
28-
```sh
29-
go generate
30-
```
31-
3222
## Bump dependencies
3323

34-
To bump all dependencies run:
24+
To bump all dependencies run in Package manager:
3525

3626
```sh
37-
go get -u -t ./...
38-
go mod tidy
27+
Update-Package -ProjectName AccountingService
3928
```

0 commit comments

Comments
 (0)