Skip to content

Commit

Permalink
add logging
Browse files Browse the repository at this point in the history
  • Loading branch information
YazeedAlKhalaf committed Jan 8, 2025
1 parent efd92d0 commit a84f3b0
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 48 deletions.
12 changes: 9 additions & 3 deletions compose.override.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ services:
- "traefik.http.routers.falak.entrypoints=web"
- "traefik.http.services.falak.loadbalancer.server.port=${FALAK_PORT}"
- "traefik.http.services.falak.loadbalancer.server.scheme=h2c"
ports:
- "9090:50064"

website:
labels:
Expand All @@ -41,13 +43,17 @@ services:
- "traefik.http.routers.grafana.entrypoints=web"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"

loki:
ports:
- "3100:3100"

plantuml:
image: plantuml/plantuml-server:jetty
labels:
- "traefik.enable=true"
- "traefik.docker.network=symmetrical-spoon_web"
- "traefik.http.routers.grafana.rule=Host(`plantuml.localhost`)"
- "traefik.http.routers.grafana.entrypoints=web"
- "traefik.http.services.grafana.loadbalancer.server.port=8080"
- "traefik.http.routers.plantuml.rule=Host(`plantuml.localhost`)"
- "traefik.http.routers.plantuml.entrypoints=web"
- "traefik.http.services.plantuml.loadbalancer.server.port=8080"
networks:
- web
33 changes: 33 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ services:
depends_on:
postgresdb:
condition: service_healthy
loki:
condition: service_started
networks:
- web
- internal
Expand All @@ -63,15 +65,46 @@ services:

grafana:
image: grafana/grafana:latest
entrypoint:
- sh
- -euc
- |
mkdir -p /etc/grafana/provisioning/datasources
cat <<EOF > /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: false
EOF
/run.sh
networks:
- web
- internal
volumes:
- grafana-storage:/var/lib/grafana
depends_on:
loki:
condition: service_started

loki:
image: grafana/loki:3.3.2
command: -config.file=/etc/loki/local-config.yaml
networks:
- internal
volumes:
- loki-storage:/loki

volumes:
postgres_data:
grafana-storage:
loki-storage:

networks:
web:
Expand Down
20 changes: 17 additions & 3 deletions falak/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
googleclient "github.com/jadwalapp/symmetrical-spoon/falak/pkg/google/client"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/httpclient"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/interceptors"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/lokilogger"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/store"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/tokens"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/util"
Expand All @@ -40,13 +41,26 @@ import (
const dbDriverName = "pgx"

func main() {
log.Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()

config, err := util.LoadFalakConfig(".")
if err != nil {
log.Fatal().Msgf("cannot load config: %v", err)
}

// ======== LOKI CLIENT ========
lokiHookConfig := lokilogger.LokiConfig{
PushIntervalSeconds: int64(config.LokiPushIntervalSeconds),
MaxBatchSize: config.LokiMaxBatchSize,
LokiEndpoint: config.LokiEndpoint,
ServiceName: "falak",
}
lokiClient := lokilogger.NewLokiClient(&lokiHookConfig)
// ======== LOKI CLIENT ========

// ======== LOGGER ========
multiLevelWriters := zerolog.MultiLevelWriter(os.Stdout, lokiClient)
log.Logger = zerolog.New(multiLevelWriters).With().Timestamp().Logger()
// ======== LOGGER ========

// ======== DATABASE ========
dbSource := util.CreateDbSource(
config.DBUser,
Expand Down Expand Up @@ -145,7 +159,7 @@ func main() {

// ======== INTERCEPTORS ========
interceptorsForServer := connect.WithInterceptors(
interceptors.LoggingInterceptor(),
interceptors.LoggingInterceptor(lokiClient),
interceptors.EnsureValidTokenInterceptor(tokens, apiMetadata),
interceptors.LangInterceptor(apiMetadata),
)
Expand Down
5 changes: 4 additions & 1 deletion falak/falak.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ SMTP_PASSWORD=
DOMAIN=http://localhost:50064
RESEND_API_KEY=
GOOGLE_CLIENT_BASE_URL=
GOOGLE_OAUTH_CLIENT_ID=
GOOGLE_OAUTH_CLIENT_ID=
LOKI_ENDPOINT=http://loki:3100
LOKI_PUSH_INTERVAL_SECONDS=1
LOKI_MAX_BATCH_SIZE=10
14 changes: 7 additions & 7 deletions falak/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
28 changes: 14 additions & 14 deletions falak/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -149,29 +149,29 @@ go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
6 changes: 4 additions & 2 deletions falak/pkg/interceptors/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (

"connectrpc.com/connect"
"github.com/google/uuid"
"github.com/jadwalapp/symmetrical-spoon/falak/pkg/lokilogger"
"github.com/rs/zerolog"
)

type traceIDKey struct{}

func LoggingInterceptor() connect.UnaryInterceptorFunc {
func LoggingInterceptor(lokiClient *lokilogger.LokiClient) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
traceID, ok := ctx.Value(traceIDKey{}).(string)
Expand All @@ -21,7 +22,8 @@ func LoggingInterceptor() connect.UnaryInterceptorFunc {
ctx = context.WithValue(ctx, traceIDKey{}, traceID)
}

logger := zerolog.New(os.Stderr).With().
multiLevelWriters := zerolog.MultiLevelWriter(os.Stdout, lokiClient)
logger := zerolog.New(multiLevelWriters).With().
Timestamp().
Str("method", req.Spec().Procedure).
Str("trace_id", traceID).
Expand Down
109 changes: 109 additions & 0 deletions falak/pkg/lokilogger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package lokilogger

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)

type LokiConfig struct {
PushIntervalSeconds int64
MaxBatchSize int
LokiEndpoint string
ServiceName string
}

type lokiStream struct {
Stream map[string]string `json:"stream"`
Values [][]string `json:"values"`
}

type lokiLogEvent struct {
Streams []lokiStream `json:"streams"`
}

type LokiClient struct {
config *LokiConfig
logs [][]string
done chan bool
}

func (l *LokiClient) bgRun() {
for {
l.sendLogsToLoki()

select {
case <-l.done:
return
default:
time.Sleep(time.Second * time.Duration(l.config.PushIntervalSeconds))
}
}
}

func (l *LokiClient) sendLogsToLoki() {
if len(l.logs) == 0 {
return
}

var logsToSend [][]string
if len(l.logs) > l.config.MaxBatchSize {
logsToSend = l.logs[:l.config.MaxBatchSize]
l.logs = l.logs[l.config.MaxBatchSize:]
} else {
logsToSend = l.logs
l.logs = [][]string{}
}

data, err := json.Marshal(lokiLogEvent{
Streams: []lokiStream{
{
Stream: map[string]string{
"service": l.config.ServiceName,
},
Values: logsToSend,
},
},
})
if err != nil {
fmt.Printf("Error marshalling logs: %v\n", err)
return
}

req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/loki/api/v1/push", l.config.LokiEndpoint), bytes.NewBuffer(data))
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}

req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending logs: %v\n", err)
return
}
defer resp.Body.Close()
}

func (l *LokiClient) Write(p []byte) (n int, err error) {
l.logs = append(l.logs, []string{
fmt.Sprintf("%d", time.Now().UnixNano()),
string(p),
})

return len(p), nil
}

func NewLokiClient(config *LokiConfig) *LokiClient {
client := &LokiClient{
config: config,
logs: [][]string{},
done: make(chan bool),
}
go client.bgRun()
return client
}
39 changes: 21 additions & 18 deletions falak/pkg/util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@ import "github.com/spf13/viper"
// FalakConfig stores all configuration of the application.
// The values are read by viper from a config file or environment variables.
type FalakConfig struct {
Port string `mapstructure:"PORT"`
JWTPublicKey string `mapstructure:"JWT_PUBLIC_KEY"`
JWTPrivateKey string `mapstructure:"JWT_PRIVATE_KEY"`
DBUser string `mapstructure:"DB_USER"`
DBPassword string `mapstructure:"DB_PASSWORD"`
DBHost string `mapstructure:"DB_HOST"`
DBPort string `mapstructure:"DB_PORT"`
DBName string `mapstructure:"DB_NAME"`
DBSSLMode string `mapstructure:"DB_SSL_MODE"`
EmailerName string `mapstructure:"EMAILER_NAME"`
SMTPHost string `mapstructure:"SMTP_HOST"`
SMTPPort string `mapstructure:"SMTP_PORT"`
SMTPUSername string `mapstructure:"SMTP_USERNAME"`
SMTPPasword string `mapstructure:"SMTP_PASSWORD"`
Domain string `mapstructure:"DOMAIN"`
ResendApiKey string `mapstructure:"RESEND_API_KEY"`
GoogleClientBaseUrl string `mapstructure:"GOOGLE_CLIENT_BASE_URL"`
GoogleOAuthClientId string `mapstructure:"GOOGLE_OAUTH_CLIENT_ID"`
Port string `mapstructure:"PORT"`
JWTPublicKey string `mapstructure:"JWT_PUBLIC_KEY"`
JWTPrivateKey string `mapstructure:"JWT_PRIVATE_KEY"`
DBUser string `mapstructure:"DB_USER"`
DBPassword string `mapstructure:"DB_PASSWORD"`
DBHost string `mapstructure:"DB_HOST"`
DBPort string `mapstructure:"DB_PORT"`
DBName string `mapstructure:"DB_NAME"`
DBSSLMode string `mapstructure:"DB_SSL_MODE"`
EmailerName string `mapstructure:"EMAILER_NAME"`
SMTPHost string `mapstructure:"SMTP_HOST"`
SMTPPort string `mapstructure:"SMTP_PORT"`
SMTPUSername string `mapstructure:"SMTP_USERNAME"`
SMTPPasword string `mapstructure:"SMTP_PASSWORD"`
Domain string `mapstructure:"DOMAIN"`
ResendApiKey string `mapstructure:"RESEND_API_KEY"`
GoogleClientBaseUrl string `mapstructure:"GOOGLE_CLIENT_BASE_URL"`
GoogleOAuthClientId string `mapstructure:"GOOGLE_OAUTH_CLIENT_ID"`
LokiEndpoint string `mapstructure:"LOKI_ENDPOINT"`
LokiPushIntervalSeconds int `mapstructure:"LOKI_PUSH_INTERVAL_SECONDS"`
LokiMaxBatchSize int `mapstructure:"LOKI_MAX_BATCH_SIZE"`
}

// LoadFalakConfig reads configuration from the provided path or environment variables.
Expand Down

0 comments on commit a84f3b0

Please sign in to comment.