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

Reduce allocs #94

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions client/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Bark Client

## IMPORTANT: It is not Yet Built

## What does the client do?
## What does the client do?
The Bark client (just _client_ henceforth) is the _library_ side of the bark. It is the piece that takes in the logs from any golang program and sends it to the server which is configured against the client. It is supposed to have the utility functions to help users log to bark directly from go code without having to worry about network calls and such.

## Levels of Logs
Expand All @@ -19,7 +17,7 @@ The client defines 7 levels of logs:
Any single character in the place of error level in a parsable single log message would indicate the level of **INFO**.

## Simple usecase
The client can be initialized and used as follows (we explain the options below code sample):
The client can be initialized and used as follows (we explain the options below code sample):

```go
barkClient := client.NewClient("<bark_server_url>", "<default_log_level>", "<default_service_name>", "<session_name>",
Expand All @@ -41,12 +39,12 @@ The options that are used for initializing the client are as follows:
- **bark_server_url**: This is the URL of a running bark server. It must end in `/`. For example `http://bark.example.com/` or `http://127.0.0.1:8080/`
- **default_log_level**: When you use `Println` or `Default`, the log message is parsed (rules for prasing are described [here](../_nocode/docs/log-string-parsing-in-bark.md)) and if it does not contain any indication for what the log level is, then the value supplied in this field is used as the log level for sent log message. When using dedicated methods for error levels (e.g. `Panic`, `Error` etc.), the parsed error level is overwritten.
- **default_service_name**: This is the name of the service which is sending the log - so it has to be the name of the program or service which is calling it. In case a blank string is sent, the value against `constants.DefaultLogServiceName` (currently set to `def_svc`) is used.
- **session_name**: This is the name of the calling app's session. This value is supposed to indicate which instance among possibly multiple instances of a service sent a log message. For example, in case of the service being deployed within Kubernetes, it might indicate the service's pod's name. If the value is sent as a blank string, client will try to use the machine's hostname. If it fails to fetch the hostname, a random string will be used instead.
- **session_name**: This is the name of the calling app's session. This value is supposed to indicate which instance among possibly multiple instances of a service sent a log message. For example, in case of the service being deployed within Kubernetes, it might indicate the service's pod's name. If the value is sent as a blank string, client will try to use the machine's hostname. If it fails to fetch the hostname, a random string will be used instead.
- **enable_slog**: This enables [slog](https://go.dev/blog/slog) for the client. When this option is enabled, all logs in addition to being sent to the bark server is also printed on STDOUT of the service.
- **enable_bulk_dispatch**: Setting this to true would enable the client to push all the requests being received in a channel and start using it. It improves the overall performance of the client sending log entries to the server.

### Simplest usecase (without any server)
The simplest usecase of any logging library is to print to STDOUT. While the primary usecase of bark is to be able to dispatch log messages to a remote server, when we start off with a new project, we often just want to start logging things to STDOUT. Maybe even later, that is how we want to use logs. For such usecases, the client library offers `NewSloggerClient` which uses the built in [slog](https://go.dev/blog/slog) package in go (version 1.21+) to log your messages to STDOUT with levels. Example:
The simplest usecase of any logging library is to print to STDOUT. While the primary usecase of bark is to be able to dispatch log messages to a remote server, when we start off with a new project, we often just want to start logging things to STDOUT. Maybe even later, that is how we want to use logs. For such usecases, the client library offers `NewSloggerClient` which uses the built in [slog](https://go.dev/blog/slog) package in go (version 1.21+) to log your messages to STDOUT with levels. Example:

```go
log := client.NewSloggerClient("INFO")
Expand All @@ -60,7 +58,7 @@ log.Info("Info message")
log.Debug("Debug message")
log.Println("Println message")
```
The above piece of code will end up printing something like the following (the dates in the beginning of each line will vary):
The above piece of code will end up printing something like the following (the dates in the beginning of each line will vary):

```
2023/10/15 21:57:41 PANIC Panic message
Expand All @@ -74,7 +72,7 @@ The above piece of code will end up printing something like the following (the d
```

## Printing logs to a file
Bark client, as shown above, is capable of sending logs to a server as well as printing them to the standard output as well. It can also do both of those things simultaneously. The architecture in very simple representation looks like this:
Bark client, as shown above, is capable of sending logs to a server as well as printing them to the standard output as well. It can also do both of those things simultaneously. The architecture in very simple representation looks like this:

![barkslogger.svg](../_nocode/images/barkslogger.svg)

Expand All @@ -100,7 +98,7 @@ The above code will write the output to `random.txt` file. You can expect the fi
2023/10/18 19:27:51 INFO Some Message that'll be sent to random.txt file
```

### Slog and writing to a file
### Slog and writing to a file

Bark client uses [slog](https://go.dev/blog/slog) internally to handle the printing of the logs. Slog is a simple and structured logging library that comes with Go (version 1.21+).

Expand Down Expand Up @@ -153,7 +151,7 @@ If you add a nil options, the log labels will appear as described in the [slog d
> LevelDebug Level = -4 \
> LevelInfo Level = 0 \
> LevelWarn Level = 4 \
> LevelError Level = 8 \
> LevelError Level = 8

The custom log levels defined by bark client have the following values:

Expand Down
12 changes: 6 additions & 6 deletions models/barklog.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ func (bld *BarkLogDao) InsertServerStartedLog() error {
}

// InsertBatch sends a batch of logs to the DB.
func (bld *BarkLogDao) InsertBatch(l []BarkLog) error {
batchOfBarkLog := [][]any{}
for i := 0; i < len(l); i++ {
batchElement := []any{l[i].LogTime, l[i].LogLevel, l[i].ServiceName, l[i].SessionName,
l[i].Code, l[i].Message, l[i].MoreData}
batchOfBarkLog = append(batchOfBarkLog, batchElement)
func (bld *BarkLogDao) InsertBatch(l *[]BarkLog) error {
batchOfBarkLog := make([][]any, len(*l))
for i := 0; i < len(*l); i++ {
batchElement := []any{(*l)[i].LogTime, (*l)[i].LogLevel, (*l)[i].ServiceName, (*l)[i].SessionName,
(*l)[i].Code, (*l)[i].Message, (*l)[i].MoreData}
batchOfBarkLog[i] = batchElement
}

_, err := resources.BarkDb.Client.CopyFrom(context.Background(), pgx.Identifier{"app_log"},
Expand Down
99 changes: 34 additions & 65 deletions services/dbLogWriter/db_log_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ func init() {
BarkLogDao = models.NewBarkLogDao()
}

var smallLogBatch = make([]models.BarkLog, constants.ServerLogInsertionBatchSizeSmall)
var mediumLogBatch = make([]models.BarkLog, constants.ServerLogInsertionBatchSizeMedium)
var largeLogBatch = make([]models.BarkLog, constants.ServerLogInsertionBatchSizeLarge)

// insertBatchOfSize saves logs of the specified size to bark database.
func insertBatchOfSize(size int, logBatch *[]models.BarkLog) {
for i := 0; i < size; i++ {
elem, ok := <-channels.LogChannel
if !ok {
fmt.Println("E#1LVMFC - Error occured while getting batch from channel")
break // Something went wrong
}
(*logBatch)[i] = elem
}

go func() {
defer resources.ServerDbSaverWg.Add(-size)
err := BarkLogDao.InsertBatch(logBatch)
if err != nil {
fmt.Println("E#1LVMIR - Batch insertion failed. Error: " + err.Error() + "\n")
for _, logEntry := range *logBatch {
fmt.Printf("E#1LVMJG - Log message: | %v\n", logEntry)
}
return
}
fmt.Printf("L#1LVM50 - Batch inserted at %s of size %d", time.Now().Format("2006-01-02 15:04:05"), size)
}()
}

// KeepSavingLogs is a go routine to check channel length and commit to DB
// The routine decides whether a batch or single insert DB call of the logs is needed to be made.
// Further bifurcation of the batch sizes is done based on the incoming traffic and LogChannel capacity.
Expand All @@ -26,79 +55,18 @@ func KeepSavingLogs() {
for {
logChannelLength = len(channels.LogChannel)
//fmt.Println("ChanLen: ", logChannelLength)
var logBatch = []models.BarkLog{}
if logChannelLength >= constants.ServerLogInsertionBatchSizeLarge {
//fmt.Println("Sending Large Batch")
// Bulk insert
for i := 0; i < constants.ServerLogInsertionBatchSizeLarge; i++ {
elem, ok := <-channels.LogChannel
if !ok {
fmt.Println("E#1LVMFC - Error occured while getting batch from channel")
break // Something went wrong
}
logBatch = append(logBatch, elem)
}

go func() {
defer resources.ServerDbSaverWg.Add(-(len(logBatch)))
err := BarkLogDao.InsertBatch(logBatch)
if err != nil {
fmt.Println("E#1LVMIR - Large Batch insertion failed. Error: " + err.Error() + "\n")
for _, logEntry := range logBatch {
fmt.Printf("E#1LVMJG - Log message: | %v\n", logEntry)
}
return
}
fmt.Println("L#1LVM50 - Large Batch inserted at ", time.Now().Format("2006-01-02 15:04:05"))
}()
insertBatchOfSize(constants.ServerLogInsertionBatchSizeLarge, &largeLogBatch)
} else if logChannelLength >= constants.ServerLogInsertionBatchSizeMedium && logChannelLength < constants.ServerLogInsertionBatchSizeLarge {
//fmt.Println("Sending Medium Batch")
// Bulk insert
for i := 0; i < constants.ServerLogInsertionBatchSizeMedium; i++ {
elem, ok := <-channels.LogChannel
if !ok {
fmt.Println("E#1LVMFF - Error occured while getting batch from channel")
break // Something went wrong
}
logBatch = append(logBatch, elem)
}

go func() {
defer resources.ServerDbSaverWg.Add(-(len(logBatch)))
err := BarkLogDao.InsertBatch(logBatch)
if err != nil {
fmt.Println("E#1LVMKR - Medium Batch insertion failed. Error: " + err.Error() + "\n")
for _, logEntry := range logBatch {
fmt.Printf("E#1LVMKU - Log message: | %v\n", logEntry)
}
return
}
fmt.Println("L#1LVMKM - Medium Batch inserted at ", time.Now().Format("2006-01-02 15:04:05"))
}()
insertBatchOfSize(constants.ServerLogInsertionBatchSizeMedium, &mediumLogBatch)
} else if logChannelLength >= constants.ServerLogInsertionBatchSizeSmall && logChannelLength < constants.ServerLogInsertionBatchSizeMedium {
//fmt.Println("Sending Small Batch")
// Bulk insert
for i := 0; i < constants.ServerLogInsertionBatchSizeSmall; i++ {
elem, ok := <-channels.LogChannel
if !ok {
fmt.Println("E#1LVMFL - Error occured while getting batch from channel")
break // Something went wrong
}
logBatch = append(logBatch, elem)
}

go func() {
defer resources.ServerDbSaverWg.Add(-(len(logBatch)))
err := BarkLogDao.InsertBatch(logBatch)
if err != nil {
fmt.Println("E#1LVMLE - Small Batch insertion failed. Error: " + err.Error() + "\n")
for _, logEntry := range logBatch {
fmt.Printf("E#1LVMLI - Log message: | %v\n", logEntry)
}
return
}
fmt.Println("L#1LVMFR - Small Batch inserted at ", time.Now().Format("2006-01-02 15:04:05"))
}()
insertBatchOfSize(constants.ServerLogInsertionBatchSizeMedium, &smallLogBatch)
} else if logChannelLength > 0 && logChannelLength < constants.ServerLogInsertionBatchSizeSmall {
//fmt.Println("Sending Single Log")
// Commit one at a time
Expand All @@ -112,6 +80,7 @@ func KeepSavingLogs() {
fmt.Printf("E#1LVMML - Log message: | %v\n", singleLog)
}
} else {
logBatch := make([]models.BarkLog, logChannelLength)
if appRuntime.ShutdownRequested.Load() == true {
if len(channels.LogChannel) == 0 {
return
Expand All @@ -124,7 +93,7 @@ func KeepSavingLogs() {
}
logBatch = append(logBatch, elem)
}
err := BarkLogDao.InsertBatch(logBatch)
err := BarkLogDao.InsertBatch(&logBatch)
if err != nil {
fmt.Println("E#1LVMN5 - Remaining Batch insertion failed. Error: " + err.Error() + "\n")
for _, logEntry := range logBatch {
Expand Down