Skip to content
Merged
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/StackAdapt/systags

go 1.19
go 1.21

require (
github.com/aws/aws-sdk-go-v2 v1.19.0
Expand Down
59 changes: 11 additions & 48 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
package main

import (
"context"
"log"
"log/slog"
"os"

"github.com/fatih/color"
"golang.org/x/exp/slog"

"github.com/StackAdapt/systags/command"
"github.com/StackAdapt/systags/manager"
"github.com/StackAdapt/systags/utility"
)

type LogHandler struct {
slog.Handler
out *log.Logger
err *log.Logger
}

func (log *LogHandler) Handle(_ context.Context, r slog.Record) error {

switch r.Level {
case slog.LevelDebug:
log.out.Println(color.HiBlackString(r.Message))

case slog.LevelInfo:
log.out.Println(r.Message)
func main() {

case slog.LevelWarn:
log.err.Println(color.YellowString(r.Message))
m := manager.NewManager()

case slog.LevelError:
log.err.Println(color.RedString(r.Message))
var level slog.Level
if os.Getenv("SYSTAGS_DEBUG") == "" {
level = slog.LevelInfo
} else {
level = slog.LevelDebug
}

return nil
}

func main() {

m := manager.NewManager()
logger := utility.NewLogger(level)
m.SetLogger(logger)

configDir := os.Getenv("SYSTAGS_CONFIG_DIR")
systemDir := os.Getenv("SYSTAGS_SYSTEM_DIR")
Expand All @@ -52,25 +34,6 @@ func main() {
m.SystemDir = systemDir
}

var level slog.Level
if os.Getenv("SYSTAGS_DEBUG") == "" {
level = slog.LevelInfo
} else {
level = slog.LevelDebug
}

loggerOpts := &slog.HandlerOptions{
Level: level,
}

logger := slog.New(&LogHandler{
Handler: slog.NewTextHandler(os.Stdout, loggerOpts),
out: log.New(os.Stdout, "", 0),
err: log.New(os.Stderr, "", 0),
})

m.SetLogger(logger)

// Perform CLI parsing, errors are logged using logger
if err := command.ParseArgs(m, os.Args); err != nil {
os.Exit(1)
Expand Down
3 changes: 1 addition & 2 deletions manager/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import (
"context"
"fmt"
"io"
"log/slog"
"strings"
"time"

"golang.org/x/exp/slog"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
Expand Down
27 changes: 15 additions & 12 deletions manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package manager
import (
"encoding/json"
"fmt"
"log/slog"
"os"
"path/filepath"
"regexp"
"strings"
"time"

"golang.org/x/exp/slog"
)

// Tags describe a map of key/value pairs
Expand Down Expand Up @@ -45,9 +44,9 @@ func NewManager() *Manager {
m := Manager{
ConfigDir: "/etc/systags.d",
SystemDir: "/var/lib/systags",
logger: slog.Default(),
}

m.SetLogger(nil)
m.Reset()

return &m
Expand Down Expand Up @@ -84,6 +83,8 @@ func (m *Manager) Reset() {
// Manager's internal config, remote, and system tags.
func (m *Manager) LoadFiles() error {

logger := m.GetLogger()

configData := make(Tags)
remoteData := make(Tags)
systemData := make(Tags)
Expand All @@ -93,7 +94,7 @@ func (m *Manager) LoadFiles() error {

if err == nil {

m.logger.Debug("reading config directory: " + m.ConfigDir)
logger.Debug("reading config directory: " + m.ConfigDir)

// Iterate through all config files
for _, file := range configFiles {
Expand All @@ -112,7 +113,7 @@ func (m *Manager) LoadFiles() error {
// Construct the full path to the current config file
configFile := filepath.Join(m.ConfigDir, file.Name())

m.logger.Debug(configFile)
logger.Debug(configFile)

// Attempt to read the contents of the file
configBytes, err := os.ReadFile(configFile)
Expand Down Expand Up @@ -141,7 +142,7 @@ func (m *Manager) LoadFiles() error {
// Check if remote file exists and then read it
if _, err := os.Stat(remoteFile); err == nil {

m.logger.Debug("reading remote file: " + remoteFile)
logger.Debug("reading remote file: " + remoteFile)

// Attempt to read the contents of the file
remoteBytes, err := os.ReadFile(remoteFile)
Expand All @@ -159,7 +160,7 @@ func (m *Manager) LoadFiles() error {
// Check if system file exists and then read it
if _, err := os.Stat(systemFile); err == nil {

m.logger.Debug("reading system file: " + systemFile)
logger.Debug("reading system file: " + systemFile)

// Attempt to read the contents of the file
systemBytes, err := os.ReadFile(systemFile)
Expand Down Expand Up @@ -187,6 +188,8 @@ func (m *Manager) LoadFiles() error {
// to create a backup of the existing files.
func (m *Manager) SaveFiles() error {

logger := m.GetLogger()

// Construct the full path to the system directory files
remoteFile := filepath.Join(m.SystemDir, "remote.json")
systemFile := filepath.Join(m.SystemDir, "system.json")
Expand All @@ -208,7 +211,7 @@ func (m *Manager) SaveFiles() error {

remoteBackup := remoteFile + ".bak"

m.logger.Debug("writing remote backup: " + remoteBackup)
logger.Debug("writing remote backup: " + remoteBackup)

// Attempt to read the contents of the file
remoteBytes, err := os.ReadFile(remoteFile)
Expand All @@ -228,7 +231,7 @@ func (m *Manager) SaveFiles() error {

systemBackup := systemFile + ".bak"

m.logger.Debug("writing system backup: " + systemBackup)
logger.Debug("writing system backup: " + systemBackup)

// Attempt to read the contents of the file
systemBytes, err := os.ReadFile(systemFile)
Expand All @@ -243,15 +246,15 @@ func (m *Manager) SaveFiles() error {
}
}

m.logger.Debug("writing remote file: " + remoteFile)
logger.Debug("writing remote file: " + remoteFile)

// Attempt to write the current tag content
err = os.WriteFile(remoteFile, remoteJson, 0666)
if err != nil {
return err
}

m.logger.Debug("writing system file: " + systemFile)
logger.Debug("writing system file: " + systemFile)

// Attempt to write the current tag content
err = os.WriteFile(systemFile, systemJson, 0666)
Expand All @@ -274,7 +277,7 @@ func (m *Manager) UpdateRemote(timeout time.Duration) error {
// which cloud provider is being used and add the feature
// to update the tags similar to how it's done now in AWS.

result, err := getAwsTags(m.logger, timeout)
result, err := getAwsTags(m.GetLogger(), timeout)
if err != nil {
return err
}
Expand Down
78 changes: 78 additions & 0 deletions utility/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package utility

import (
"context"
"log"
"log/slog"
"os"

"github.com/fatih/color"
)

// ColorWriter is a custom writer that adds color to the output.
type ColorWriter struct {
w *os.File
color *color.Color
}

func (cw *ColorWriter) Write(p []byte) (n int, err error) {
return cw.w.Write([]byte(cw.color.Sprint(string(p))))
}

// LogHandler is a custom slog handler that routes log messages to different
// loggers based on their severity level. It embeds slog.Handler and defines
// loggers for different severity levels like Debug, Info, Warn, and Error.
type LogHandler struct {
slog.Handler
Debug *log.Logger
Info *log.Logger
Warn *log.Logger
Error *log.Logger
}

func (log *LogHandler) Handle(_ context.Context, r slog.Record) error {

switch r.Level {
case slog.LevelDebug:
log.Debug.Println(r.Message)

case slog.LevelInfo:
log.Info.Println(r.Message)

case slog.LevelWarn:
log.Warn.Println(r.Message)

case slog.LevelError:
log.Error.Println(r.Message)
}

return nil
}

// NewLogger creates a new instance of slog.Logger. The logging level can be
// specified by level. The returned logger writes info messages to STDOUT and
// warning, error, and debug messages to STDERR. The messages are color-coded
// for readability: debug messages are light gray, warning messages are yellow,
// and error messages are red.
func NewLogger(level slog.Level) *slog.Logger {

options := &slog.HandlerOptions{
Level: level,
}

cwDebug := &ColorWriter{w: os.Stderr, color: color.New(color.FgHiBlack)}
cwInfo := &ColorWriter{w: os.Stdout, color: color.New()}
cwWarn := &ColorWriter{w: os.Stderr, color: color.New(color.FgYellow)}
cwError := &ColorWriter{w: os.Stderr, color: color.New(color.FgRed)}

logger := slog.New(&LogHandler{
Handler: slog.NewTextHandler(os.Stdout, options),

Debug: log.New(cwDebug, "", 0),
Info: log.New(cwInfo, "", 0),
Warn: log.New(cwWarn, "", 0),
Error: log.New(cwError, "", 0),
})

return logger
}