Skip to content

Commit

Permalink
refactor logger
Browse files Browse the repository at this point in the history
  • Loading branch information
aradwann committed Apr 19, 2024
1 parent e3b8571 commit fce9fc8
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 227 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ To contribute to eEnergy, you will need to set up your development environment w
- Ensure all required tools are installed.

3. **Generate Certificates For Local Use**
- ```bash

- ```bash
make init
```
- ```bash

- ```bash
make gen-cert
```

4. **Build and Run Using Docker**

- Build the Docker images:

```bash
Expand All @@ -64,10 +67,11 @@ To contribute to eEnergy, you will need to set up your development environment w
```

### using postman with mTLS enabled

1. add CA pem
2. add client certificate with crt, key files and localhost:9091 as domain

4. **Access the Application**
3. **Access the Application**
- The application and its services are now accessible on your local machine.

## Contributing
Expand All @@ -91,4 +95,4 @@ By contributing to eEnergy, you're helping to create a more sustainable and effi
- improve logging and configure graphana
- explore pprof
- impl nearest energy source endpoint (input: current location, output: nearest energy source details)
- watch kubernetes section and understand it -->
- watch kubernetes and docker swarm section and understand it -->
32 changes: 14 additions & 18 deletions db/store/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,54 @@ import (
"context"
"database/sql"
"fmt"
"log/slog"
"strings"
)

// DBTX defines the interface for database transactions.
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}

// New creates a new instance of Queries using the provided database transaction.
func New(db DBTX) *Queries {
return &Queries{db: db}
}

// Queries provides methods for executing database queries.
type Queries struct {
db DBTX
}

// type storedProcedureParams struct {
// InParams []interface{}
// OutParams []interface{}
// }

// func (q *Queries) callStoredProcedure(ctx context.Context, procedureName string, params storedProcedureParams) *sql.Row {
// sqlStatement := fmt.Sprintf(`CALL %s(%s)`, procedureName, generateParamPlaceholders(len(params.InParams)))

// return q.db.QueryRowContext(
// ctx,
// sqlStatement,
// params.InParams...,
// )
// }
// callStoredFunction executes a stored function and returns a single row result.
func (q *Queries) callStoredFunction(ctx context.Context, functionName string, params ...interface{}) *sql.Row {
// Assuming generateParamPlaceholders generates the placeholders for parameters
placeholders := generateParamPlaceholders(len(params))

// Use SELECT statement to call the stored function
// Construct the SQL statement to call the stored function.
sqlStatement := fmt.Sprintf(`SELECT * FROM %s(%s)`, functionName, placeholders)
slog.Info("PostgreSQL function called",
slog.String("function name", functionName),
// slog.Any("params", params), // TODO: filter out sensitive info
slog.String("SQL statement", sqlStatement),
)

return q.db.QueryRowContext(ctx, sqlStatement, params...)
}

// callStoredFunctionRows executes a stored function and returns multiple rows result.
func (q *Queries) callStoredFunctionRows(ctx context.Context, functionName string, params ...interface{}) (*sql.Rows, error) {
// Assuming generateParamPlaceholders generates the placeholders for parameters
placeholders := generateParamPlaceholders(len(params))

// Use SELECT statement to call the stored function
// Construct the SQL statement to call the stored function.
sqlStatement := fmt.Sprintf(`SELECT * FROM %s(%s)`, functionName, placeholders)

return q.db.QueryContext(ctx, sqlStatement, params...)
}

// generateParamPlaceholders generates placeholders for SQL parameters.
func generateParamPlaceholders(count int) string {
placeholders := make([]string, count)
for i := 1; i <= count; i++ {
Expand Down
42 changes: 42 additions & 0 deletions db/store/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package db

import (
"database/sql"
"fmt"
"log/slog"
"os"

"github.com/aradwann/eenergy/util"
)

// InitDatabase initializes the database connection and performs migrations.
func InitDatabase(config util.Config) Store {
// Initialize database connection.
dbConn, err := initDatabaseConn(config)
if err != nil {
handleError("Unable to connect to database", err)
}
defer dbConn.Close()

// Run database migrations.
RunDBMigrations(dbConn, config.MigrationsURL)

// Create store instance.
store := newStore(dbConn)
return store
}

// initDatabaseConn initializes the database connection.
func initDatabaseConn(config util.Config) (*sql.DB, error) {
dbConn, err := sql.Open(config.DBDriver, config.DBSource)
if err != nil {
handleError("Unable to connect to database", err)
}
return dbConn, err
}

// handleError logs an error message and exits the program with status code 1.
func handleError(message string, err error) {
slog.Error(fmt.Sprintf("%s: %v", message, err))
os.Exit(1)
}
2 changes: 1 addition & 1 deletion db/store/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ func TestMain(m *testing.M) {
log.Fatal("cannot connect to db:", err)
}

testStore = NewStore(testDB)
testStore = newStore(testDB)
os.Exit(m.Run())
}
4 changes: 2 additions & 2 deletions db/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type SQLStore struct {
db *sql.DB
}

// NewStore create a new Store
func NewStore(db *sql.DB) Store {
// newStore create a new Store
func newStore(db *sql.DB) Store {
return &SQLStore{
db: db,
Queries: New(db),
Expand Down
17 changes: 13 additions & 4 deletions db/store/tx_create_user.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
package db

import "context"
import (
"context"
"log/slog"
)

// CreateUserTxParams represents the parameters for creating a user transaction.
type CreateUserTxParams struct {
CreateUserParams
AfterCreate func(user User) error
CreateUserParams // Embedding CreateUserParams for reuse
AfterCreate func(user User) error
}

// CreateUserTxResult represents the result of creating a user transaction.
type CreateUserTxResult struct {
User User
}

// CreateUserTx executes a transaction for creating a user.
func (store *SQLStore) CreateUserTx(ctx context.Context, arg CreateUserTxParams) (CreateUserTxResult, error) {
slog.Info("Create User Transaction")
var result CreateUserTxResult

err := store.execTx(ctx, func(q *Queries) error {
var err error

// Create user using provided parameters.
result.User, err = q.CreateUser(ctx, arg.CreateUserParams)
if err != nil {
return err
}
return arg.AfterCreate(result.User)

// Execute user-defined callback after creating the user.
return arg.AfterCreate(result.User)
})

return result, err
Expand Down
3 changes: 3 additions & 0 deletions db/store/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"log"
"log/slog"
)

type CreateUserParams struct {
Expand All @@ -16,6 +17,7 @@ type CreateUserParams struct {

func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
var user User
slog.Info("CreateUser", slog.String("username", arg.Username))
row := q.callStoredFunction(ctx, "create_user",
arg.Username,
arg.HashedPassword,
Expand All @@ -24,6 +26,7 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e

err := scanUserFromRow(row, &user)
if err != nil {
slog.Error("error scaning the created user", slog.String("error message", err.Error()))
return user, err
}
return user, nil
Expand Down
32 changes: 7 additions & 25 deletions gapi/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package gapi
import (
"context"
"net/http"
"os"
"time"

"google.golang.org/grpc"
Expand All @@ -13,7 +12,7 @@ import (
"log/slog"
)

// GrpcLogger logs gRPC requests and responses.
// GrpcLogger is a unary server interceptor that logs gRPC requests and responses.
func GrpcLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
startTime := time.Now()
res, err := handler(ctx, req)
Expand All @@ -34,8 +33,8 @@ func GrpcLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo

slog.LogAttrs(context.Background(),
logLevel,
"received grpc req",
slog.String("protocol", "grpc"),
"received gRPC request",
slog.String("protocol", "gRPC"),
slog.String("method", info.FullMethod),
slog.Int("status_code", int(statusCode)),
slog.String("status_text", statusCode.String()),
Expand All @@ -46,7 +45,7 @@ func GrpcLogger(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo
return res, err
}

// HttpLogger logs HTTP requests and responses.
// HttpLogger is a middleware that logs HTTP requests and responses.
func HttpLogger(handler http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
startTime := time.Now()
Expand All @@ -68,8 +67,8 @@ func HttpLogger(handler http.Handler) http.Handler {

slog.LogAttrs(context.Background(),
logLevel,
"received http req",
slog.String("protocol", "http"),
"received HTTP request",
slog.String("protocol", "HTTP"),
slog.String("method", req.Method),
slog.String("uri", req.RequestURI),
slog.Int("status_code", rec.StatusCode),
Expand All @@ -80,7 +79,7 @@ func HttpLogger(handler http.Handler) http.Handler {
})
}

// ResponseRecorder is used to get the status code from the original response writer.
// ResponseRecorder is used to capture the status code and response body.
type ResponseRecorder struct {
http.ResponseWriter
StatusCode int
Expand All @@ -98,20 +97,3 @@ func (rec *ResponseRecorder) Write(body []byte) (int, error) {
rec.Body = body
return rec.ResponseWriter.Write(body)
}

func NewDevelopmentLoggerHandler() slog.Handler {
return slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
AddSource: false,
Level: nil,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey && len(groups) == 0 {
return slog.Attr{}
}
return a
},
})
}

func NewProductionLoggerHandler() slog.Handler {
return slog.NewJSONHandler(os.Stdout, nil)
}

0 comments on commit fce9fc8

Please sign in to comment.