Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change AccountService from Go to DotNet (auto) #1538

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ the release.
([#1515](https://github.com/open-telemetry/opentelemetry-demo/pull/1486))
* [frontend] Fix imageloading issues on optimized images. bump next.js version
([#1571](https://github.com/open-telemetry/opentelemetry-demo/pull/1571))
* [accountingservice] convert from Go service to .NET service, uses
OpenTelemetry .NET Automatic Instrumentation.
([#1538](https://github.com/open-telemetry/opentelemetry-demo/pull/1538))

## 1.9.0

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.3.0" />
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
<PackageReference Include="Grpc.Tools" Version="2.62.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.6.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
```
47 changes: 0 additions & 47 deletions src/accountingservice/go.mod

This file was deleted.