Skip to content

Commit 2bee38f

Browse files
committed
Added metrics support to examples and observability stack
1 parent 601738a commit 2bee38f

File tree

8 files changed

+189
-9
lines changed

8 files changed

+189
-9
lines changed

example/1.6/cp/charge_point_sim.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ import (
88
"strconv"
99
"time"
1010

11-
"github.com/lorenzodonini/ocpp-go/ocpp1.6/logging"
12-
"github.com/sirupsen/logrus"
13-
1411
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
1512
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
1613
"github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth"
14+
"github.com/lorenzodonini/ocpp-go/ocpp1.6/logging"
1715
"github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
1816
"github.com/lorenzodonini/ocpp-go/ocppj"
1917
"github.com/lorenzodonini/ocpp-go/ws"
18+
"github.com/sirupsen/logrus"
2019
)
2120

2221
const (

example/1.6/cs/central_system_sim.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package main
22

33
import (
4+
"context"
45
"crypto/tls"
56
"crypto/x509"
67
"os"
78
"strconv"
89
"time"
910

11+
"github.com/pkg/errors"
1012
"github.com/sirupsen/logrus"
13+
"go.opentelemetry.io/otel"
14+
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
15+
metricsdk "go.opentelemetry.io/otel/sdk/metric"
16+
"go.opentelemetry.io/otel/sdk/resource"
17+
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
18+
"google.golang.org/grpc"
19+
"google.golang.org/grpc/credentials/insecure"
1120

1221
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
1322
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
@@ -28,6 +37,8 @@ const (
2837
envVarCaCertificate = "CA_CERTIFICATE_PATH"
2938
envVarServerCertificate = "SERVER_CERTIFICATE_PATH"
3039
envVarServerCertificateKey = "SERVER_CERTIFICATE_KEY_PATH"
40+
envVarMetricsEnabled = "METRICS_ENABLED"
41+
envVarMetricsAddress = "METRICS_ADDRESS"
3142
)
3243

3344
var log *logrus.Logger
@@ -188,6 +199,54 @@ func exampleRoutine(chargePointID string, handler *CentralSystemHandler) {
188199
}
189200
}
190201

202+
// sets up OTLP metrics exporter
203+
func setupMetrics(address string) error {
204+
grpcOpts := []grpc.DialOption{
205+
grpc.WithTransportCredentials(insecure.NewCredentials()),
206+
}
207+
208+
client, err := grpc.NewClient(address, grpcOpts...)
209+
210+
if err != nil {
211+
return errors.Wrap(err, "failed to create gRPC connection to collector")
212+
}
213+
214+
ctx := context.Background()
215+
216+
exporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithGRPCConn(client))
217+
if err != nil {
218+
return errors.Wrap(err, "failed to create otlp metric exporter")
219+
}
220+
221+
resource, err := resource.New(ctx,
222+
resource.WithAttributes(
223+
semconv.ServiceNameKey.String("centralSystem-demo"),
224+
semconv.ServiceVersionKey.String("example"),
225+
),
226+
resource.WithFromEnv(),
227+
resource.WithContainer(),
228+
resource.WithOS(),
229+
resource.WithOSType(),
230+
resource.WithHost(),
231+
)
232+
if err != nil {
233+
return errors.Wrap(err, "failed to create resource")
234+
}
235+
236+
meterProvider := metricsdk.NewMeterProvider(
237+
metricsdk.WithReader(
238+
metricsdk.NewPeriodicReader(
239+
exporter,
240+
metricsdk.WithInterval(10*time.Second),
241+
),
242+
),
243+
metricsdk.WithResource(resource),
244+
)
245+
246+
otel.SetMeterProvider(meterProvider)
247+
return nil
248+
}
249+
191250
// Start function
192251
func main() {
193252
// Load config from ENV
@@ -198,6 +257,16 @@ func main() {
198257
} else {
199258
log.Printf("no valid %v environment variable found, using default port", envVarServerPort)
200259
}
260+
261+
// Setup metrics if enabled
262+
if t, _ := os.LookupEnv(envVarMetricsEnabled); t == "true" {
263+
address, _ := os.LookupEnv(envVarMetricsAddress)
264+
if err := setupMetrics(address); err != nil {
265+
log.Error(err)
266+
return
267+
}
268+
}
269+
201270
// Check if TLS enabled
202271
t, _ := os.LookupEnv(envVarTls)
203272
tlsEnabled, _ := strconv.ParseBool(t)
@@ -207,6 +276,7 @@ func main() {
207276
} else {
208277
centralSystem = setupCentralSystem()
209278
}
279+
210280
// Support callbacks for all OCPP 1.6 profiles
211281
handler := &CentralSystemHandler{chargePoints: map[string]*ChargePointState{}}
212282
centralSystem.SetCoreHandler(handler)

example/1.6/docker-compose.tls.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: '3'
1+
version: '3.9'
22
services:
33
central-system:
44
build:
@@ -15,6 +15,8 @@ services:
1515
- CA_CERTIFICATE_PATH=/usr/local/share/certs/ca.crt
1616
- SERVER_CERTIFICATE_PATH=/usr/local/share/certs/central-system.crt
1717
- SERVER_CERTIFICATE_KEY_PATH=/usr/local/share/certs/central-system.key
18+
- METRICS_ENABLED=${METRICS_ENABLED:-false}
19+
- METRICS_ADDRESS=${METRICS_ADDRESS:-lgtm-stack:4317}
1820
ports:
1921
- "443:443"
2022
networks:

example/1.6/docker-compose.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
version: '3'
1+
version: '3.9'
22
services:
33
central-system:
44
build:
55
context: ../..
6-
dockerfile: cs/Dockerfile
6+
dockerfile: example/1.6/cs/Dockerfile
77
image: ldonini/ocpp1.6-central-system:latest
88
container_name: central-system
99
environment:
10+
- METRICS_ENABLED=${METRICS_ENABLED:-false}
11+
- METRICS_ADDRESS=${METRICS_ADDRESS:-lgtm-stack:4317}
1012
- SERVER_LISTEN_PORT=8887
1113
ports:
1214
- "8887:8887"
@@ -19,7 +21,7 @@ services:
1921
condition: service_started
2022
build:
2123
context: ../..
22-
dockerfile: cp/Dockerfile
24+
dockerfile: example/1.6/cp/Dockerfile
2325
image: ldonini/ocpp1.6-charge-point:latest
2426
container_name: charge-point
2527
environment:

example/2.0.1/csms/csms_sim.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
package main
22

33
import (
4+
"context"
45
"crypto/tls"
56
"crypto/x509"
67
"fmt"
78
"os"
89
"strconv"
910
"time"
1011

12+
"github.com/pkg/errors"
1113
"github.com/sirupsen/logrus"
14+
"go.opentelemetry.io/otel"
15+
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
16+
metricsdk "go.opentelemetry.io/otel/sdk/metric"
17+
"go.opentelemetry.io/otel/sdk/resource"
18+
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
19+
"google.golang.org/grpc"
20+
"google.golang.org/grpc/credentials/insecure"
1221

1322
"github.com/lorenzodonini/ocpp-go/ocpp2.0.1"
1423
"github.com/lorenzodonini/ocpp-go/ocpp2.0.1/availability"
@@ -31,6 +40,8 @@ const (
3140
envVarCaCertificate = "CA_CERTIFICATE_PATH"
3241
envVarServerCertificate = "SERVER_CERTIFICATE_PATH"
3342
envVarServerCertificateKey = "SERVER_CERTIFICATE_KEY_PATH"
43+
envVarMetricsEnabled = "METRICS_ENABLED"
44+
envVarMetricsAddress = "METRICS_ADDRESS"
3445
)
3546

3647
var log *logrus.Logger
@@ -246,6 +257,54 @@ func exampleRoutine(chargingStationID string, handler *CSMSHandler) {
246257
// Finish simulation
247258
}
248259

260+
// sets up OTLP metrics exporter
261+
func setupMetrics(address string) error {
262+
grpcOpts := []grpc.DialOption{
263+
grpc.WithTransportCredentials(insecure.NewCredentials()),
264+
}
265+
266+
client, err := grpc.NewClient(address, grpcOpts...)
267+
268+
if err != nil {
269+
return errors.Wrap(err, "failed to create gRPC connection to collector")
270+
}
271+
272+
ctx := context.Background()
273+
274+
exporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithGRPCConn(client))
275+
if err != nil {
276+
return errors.Wrap(err, "failed to create otlp metric exporter")
277+
}
278+
279+
resource, err := resource.New(ctx,
280+
resource.WithAttributes(
281+
semconv.ServiceNameKey.String("csms-demo"),
282+
semconv.ServiceVersionKey.String("example"),
283+
),
284+
resource.WithFromEnv(),
285+
resource.WithContainer(),
286+
resource.WithOS(),
287+
resource.WithOSType(),
288+
resource.WithHost(),
289+
)
290+
if err != nil {
291+
return errors.Wrap(err, "failed to create resource")
292+
}
293+
294+
meterProvider := metricsdk.NewMeterProvider(
295+
metricsdk.WithReader(
296+
metricsdk.NewPeriodicReader(
297+
exporter,
298+
metricsdk.WithInterval(10*time.Second),
299+
),
300+
),
301+
metricsdk.WithResource(resource),
302+
)
303+
304+
otel.SetMeterProvider(meterProvider)
305+
return nil
306+
}
307+
249308
// Start function
250309
func main() {
251310
// Load config from ENV
@@ -256,6 +315,16 @@ func main() {
256315
} else {
257316
log.Printf("no valid %v environment variable found, using default port", envVarServerPort)
258317
}
318+
319+
// Setup metrics if enabled
320+
if t, _ := os.LookupEnv(envVarMetricsEnabled); t == "true" {
321+
address, _ := os.LookupEnv(envVarMetricsAddress)
322+
if err := setupMetrics(address); err != nil {
323+
log.Error(err)
324+
return
325+
}
326+
}
327+
259328
// Check if TLS enabled
260329
t, _ := os.LookupEnv(envVarTls)
261330
tlsEnabled, _ := strconv.ParseBool(t)
@@ -265,6 +334,7 @@ func main() {
265334
} else {
266335
csms = setupCentralSystem()
267336
}
337+
268338
// Support callbacks for all OCPP 2.0.1 profiles
269339
handler := &CSMSHandler{chargingStations: map[string]*ChargingStationState{}}
270340
csms.SetAuthorizationHandler(handler)

example/2.0.1/docker-compose.tls.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ services:
1515
- CA_CERTIFICATE_PATH=/usr/local/share/certs/ca.crt
1616
- SERVER_CERTIFICATE_PATH=/usr/local/share/certs/csms.crt
1717
- SERVER_CERTIFICATE_KEY_PATH=/usr/local/share/certs/csms.key
18+
- METRICS_ENABLED=${METRICS_ENABLED:-false}
19+
- METRICS_ADDRESS=${METRICS_ADDRESS:-lgtm-stack:4317}
1820
ports:
1921
- "443:443"
2022
networks:

example/2.0.1/docker-compose.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ services:
33
csms:
44
build:
55
context: ../..
6-
dockerfile: csms/Dockerfile
6+
dockerfile: example/2.0.1/csms/Dockerfile
77
image: ldonini/ocpp2.0.1-csms:latest
88
container_name: csms
99
environment:
1010
- SERVER_LISTEN_PORT=8887
11+
- METRICS_ENABLED=${METRICS_ENABLED:-false}
12+
- METRICS_ADDRESS=${METRICS_ADDRESS:-lgtm-stack:4317}
1113
ports:
1214
- "8887:8887"
1315
networks:
@@ -16,7 +18,7 @@ services:
1618
charging-station:
1719
build:
1820
context: ../..
19-
dockerfile: chargingstation/Dockerfile
21+
dockerfile: example/2.0.1/chargingstation/Dockerfile
2022
image: ldonini/ocpp2.0.1-chargingstation:latest
2123
container_name: charging-station
2224
environment:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
services:
2+
# Grafana stack is used for CPMS observability purposes.
3+
# For capturing metrics, you can configure the exporters via environment variables
4+
# to either Prometheus or OTel Collector (default).
5+
# Prometheus needs to be configured so it can scrape your application instance(s).
6+
# To simplify testing and development, we will use the Otel collector as the default.
7+
# Your CPMS backend will push the metrics in OTLP format to the Otel collector.
8+
# The collector comes as a part of the LGTM stack.
9+
grafana-lgtm-stack:
10+
image: grafana/otel-lgtm
11+
container_name: lgtm-stack
12+
hostname: lgtm-stack
13+
#environment:
14+
# - GF_SECURITY_ADMIN_USER="admin"
15+
# - GF_SECURITY_ADMIN_PASSWORD="admin"
16+
volumes:
17+
- prometheus:/prometheus
18+
- loki:/data/loki
19+
- grafana:/var/lib/grafana
20+
networks:
21+
- sim
22+
ports:
23+
- "3000:3000"
24+
- "4317:4317"
25+
26+
networks:
27+
sim:
28+
driver: bridge
29+
30+
volumes:
31+
prometheus:
32+
loki:
33+
grafana:

0 commit comments

Comments
 (0)