Skip to content

Commit

Permalink
Change AccountService from Go to DotNet (auto) (#1538)
Browse files Browse the repository at this point in the history
* 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 <juliano.costa@datadoghq.com>
Co-authored-by: Juliano Costa <julianocosta89@outlook.com>
  • Loading branch information
3 people authored Jun 27, 2024
1 parent 918e86a commit 5400099
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 519 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ the release.

## Unreleased

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

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ services:
deploy:
resources:
limits:
memory: 20M
memory: 50M
restart: unless-stopped
environment:
- KAFKA_SERVICE_ADDR
- OTEL_EXPORTER_OTLP_ENDPOINT
- OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_HTTP}
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
- OTEL_RESOURCE_ATTRIBUTES
- OTEL_SERVICE_NAME=accountingservice
Expand Down
32 changes: 32 additions & 0 deletions src/accountingservice/AccountingService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Confluent.Kafka" Version="2.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.27.1" />
<PackageReference Include="Grpc.Tools" Version="2.64.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="OpenTelemetry.AutoInstrumentation" Version="1.7.0" />
</ItemGroup>

<ItemGroup>
<!-- GrpcServices is 'none' so that we do not need to depend on the grpc nuget package, and we only need protobuf support. -->
<Protobuf Include="proto\demo.proto" GrpcServices="none" />
</ItemGroup>

<ItemGroup>
<Folder Include="proto\" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions src/accountingservice/AccountingService.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34701.34
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccountingService", "AccountingService.csproj", "{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C66C35E2-DF04-4DCF-8F6A-87B6D6433FF6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6340CDDC-E917-4532-A056-5526E0A7BDDA}
EndGlobalSection
EndGlobal
93 changes: 93 additions & 0 deletions src/accountingservice/Consumer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Confluent.Kafka;
using Microsoft.Extensions.Logging;
using Oteldemo;

namespace AccountingService;

internal class Consumer : IDisposable
{
private const string TopicName = "orders";

private ILogger _logger;
private IConsumer<string, byte[]> _consumer;
private bool _isListening;

public Consumer(ILogger<Consumer> logger)
{
_logger = logger;

var servers = Environment.GetEnvironmentVariable("KAFKA_SERVICE_ADDR")
?? throw new ArgumentNullException("KAFKA_SERVICE_ADDR");

_consumer = BuildConsumer(servers);
_consumer.Subscribe(TopicName);

_logger.LogInformation($"Connecting to Kafka: {servers}");
}

public void StartListening()
{
_isListening = true;

try
{
while (_isListening)
{
try
{
var consumeResult = _consumer.Consume();

ProcessMessage(consumeResult.Message);
}
catch (ConsumeException e)
{
_logger.LogError(e, "Consume error: {0}", e.Error.Reason);
}
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Closing consumer");

_consumer.Close();
}
}

private void ProcessMessage(Message<string, byte[]> message)
{
try
{
var order = OrderResult.Parser.ParseFrom(message.Value);

Log.OrderReceivedMessage(_logger, order);
}
catch (Exception ex)
{
_logger.LogError(ex, "Order parsing failed:");
}
}

private IConsumer<string, byte[]> BuildConsumer(string servers)
{
var conf = new ConsumerConfig
{
GroupId = $"accountingservice",
BootstrapServers = servers,
// https://github.com/confluentinc/confluent-kafka-dotnet/tree/07de95ed647af80a0db39ce6a8891a630423b952#basic-consumer-example
AutoOffsetReset = AutoOffsetReset.Earliest,
EnableAutoCommit = true
};

return new ConsumerBuilder<string, byte[]>(conf)
.Build();
}

public void Dispose()
{
_isListening = false;
_consumer?.Dispose();
}
}
57 changes: 28 additions & 29 deletions src/accountingservice/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0


FROM golang:1.22-alpine AS builder

WORKDIR /usr/src/app

RUN apk update \
&& apk add --no-cache make protobuf-dev

RUN --mount=type=cache,target=/go/pkg/mod/ \
--mount=type=bind,source=./src/accountingservice/go.sum,target=go.sum \
--mount=type=bind,source=./src/accountingservice/go.mod,target=go.mod \
--mount=type=bind,source=./src/accountingservice/tools.go,target=tools.go \
go mod download \
&& go list -e -f '{{range .Imports}}{{.}} {{end}}' tools.go | CGO_ENABLED=0 xargs go install -mod=readonly

RUN --mount=type=cache,target=/go/pkg/mod/ \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=bind,rw,source=./src/accountingservice,target=. \
--mount=type=bind,rw,source=./pb,target=./pb \
protoc -I ./pb ./pb/demo.proto --go_out=./ --go-grpc_out=./ \
&& go build -ldflags "-s -w" -o /go/bin/accountingservice/ ./

FROM alpine

WORKDIR /usr/src/app/

COPY --from=builder /go/bin/accountingservice/ ./

ENTRYPOINT [ "./accountingservice" ]
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["/src/accountingservice/", "AccountingService/"]
COPY ["/pb/demo.proto", "AccountingService/proto/"]
RUN dotnet restore "./AccountingService/AccountingService.csproj"
WORKDIR "/src/AccountingService"

RUN dotnet build "./AccountingService.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./AccountingService.csproj" --use-current-runtime -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

USER root
RUN mkdir -p "/var/log/opentelemetry/dotnet"
RUN chown app "/var/log/opentelemetry/dotnet"
USER app

ENTRYPOINT ["./instrument.sh", "dotnet", "AccountingService.dll"]
34 changes: 34 additions & 0 deletions src/accountingservice/Helpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Collections;

namespace AccountingService
{
internal static class Helpers
{
private static List<string> RelevantPrefixes = ["DOTNET_", "CORECLR_", "OTEL_", "KAFKA_"];

public static IEnumerable<DictionaryEntry> FilterRelevant(this IDictionary envs)
{
foreach (DictionaryEntry env in envs)
{
foreach (var prefix in RelevantPrefixes)
{
if (env.Key.ToString()?.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase) ?? false)
{
yield return env;
}
}
}
}

public static void OutputInOrder(this IEnumerable<DictionaryEntry> envs)
{
foreach (var env in envs.OrderBy(x => x.Key))
{
Console.WriteLine(env);
}
}
}
}
16 changes: 16 additions & 0 deletions src/accountingservice/Log.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using Microsoft.Extensions.Logging;
using Oteldemo;

namespace AccountingService
{
internal static partial class Log
{
[LoggerMessage(
Level = LogLevel.Information,
Message = "Order details: {@OrderResult}.")]
public static partial void OrderReceivedMessage(ILogger logger, OrderResult orderResult);
}
}
24 changes: 24 additions & 0 deletions src/accountingservice/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using AccountingService;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Console.WriteLine("Accounting service started");

Environment.GetEnvironmentVariables()
.FilterRelevant()
.OutputInOrder();

var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddSingleton<Consumer>();
})
.Build();

var consumer = host.Services.GetRequiredService<Consumer>();
consumer.StartListening();

host.Run();
19 changes: 4 additions & 15 deletions src/accountingservice/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ This service consumes new orders from a Kafka topic.
To build the service binary, run:

```sh
go build -o /go/bin/accountingservice/
cp pb/demo.proto src/accoutingservice/proto/demo.proto # root context
dotnet build # accounting service context
```

## Docker Build
Expand All @@ -18,22 +19,10 @@ From the root directory, run:
docker compose build accountingservice
```

## Regenerate protos

> [!NOTE]
> [`protoc`](https://grpc.io/docs/protoc-installation/) is required.
To regenerate gRPC code run:

```sh
go generate
```

## Bump dependencies

To bump all dependencies run:
To bump all dependencies run in Package manager:

```sh
go get -u -t ./...
go mod tidy
Update-Package -ProjectName AccountingService
```
Loading

0 comments on commit 5400099

Please sign in to comment.