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
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ jobs:
matrix:
platform: [ubuntu-latest]
go-version:
- 1.22.x
- 1.23.x
- 1.24.x
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
Expand Down
62 changes: 62 additions & 0 deletions collections/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,68 @@ func Append[T any](slices ...[]T) []T {
return output
}

func MatchAny(items []string, patterns ...string) (matches, negated bool) {
var matched = false
for _, item := range items {
matches, negated = MatchItem(item, patterns...)
matched = matched || matches
if negated {
return false, true
}
}

return matched, false
}

// matchItems returns true if any of the patterns in the list match the item.
// negative matches are supported by prefixing the item with a "!" and
// takes precendence over positive match.
// * matches everything
// to match prefix and suffix use "*" accordingly.
func MatchItem(item string, patterns ...string) (matches, negated bool) {
if len(patterns) == 0 {
return true, false
}

slices.SortFunc(patterns, sortPatterns)

//process negations first
for _, p := range patterns {
pattern, err := url.QueryUnescape(strings.TrimSpace(p))
if err != nil {
continue
}

if strings.HasPrefix(pattern, "!") {
if matchPattern(item, strings.TrimPrefix(pattern, "!")) {
return false, true
}
}

}

// then normal filters
for _, p := range patterns {
pattern, err := url.QueryUnescape(strings.TrimSpace(p))
if err != nil {
continue
}

if matchPattern(item, pattern) {
return true, false
}
}

//nolint:gosimple
//lint:ignore S1008 ...
if IsExclusionOnlyPatterns(patterns) {
// If all the filters were exlusions, and none of the exclusions excluded the item, then it's a match
return true, false
}

return false, false
}

// matchItems returns true if any of the patterns in the list match the item.
// negative matches are supported by prefixing the item with a "!" and
// takes precendence over positive match.
Expand Down
10 changes: 10 additions & 0 deletions deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ var dependencies = map[string]Dependency{
MacosxARM: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-macos-aarch64.tar.xz",
BinaryName: "postgrest",
},

"postgrestV13": {
Version: "v13.0.5",
Linux: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-linux-static-x86-64.tar.xz",
LinuxARM: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-ubuntu-aarch64.tar.xz ",
Windows: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-windows-x86-64.zip",
Macosx: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-macos-x86-64.tar.xz",
MacosxARM: "https://github.com/PostgREST/postgrest/releases/download/{{.version}}/postgrest-{{.version}}-macos-aarch64.tar.xz",
BinaryName: "postgrest",
},
"yq": {
Version: "v4.16.2",
Template: "https://github.com/mikefarah/yq/releases/download/{{.version}}/yq_{{.os}}_{{.platform}}",
Expand Down
24 changes: 23 additions & 1 deletion http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"net/url"
"strings"
"time"

"github.com/flanksource/commons/console"
"github.com/flanksource/commons/logger"
)

// Request represents an HTTP request that can be customized and executed.
Expand Down Expand Up @@ -161,7 +164,7 @@ func (r *Request) Retry(maxRetries uint, baseDuration time.Duration, exponent fl
// // JSON body
// req.Body(map[string]string{"key": "value"})
//
// // String body
// // String body
// req.Body("raw text data")
//
// // Reader body
Expand Down Expand Up @@ -229,3 +232,22 @@ func (r *Request) do() (resp *Response, err error) {
return response, nil
}
}

func (r *Request) HeaderMap() map[string]string {
headers := make(map[string]string)
for k, v := range r.headers {
headers[k] = strings.Join(v, ", ")
}
return headers
}

func (r *Request) Debug() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("%s %s\n", r.method, logger.StripSecrets(r.url.String())))
for k, v := range logger.StripSecretsFromMap(r.HeaderMap()) {
sb.WriteString(fmt.Sprintf(" %s: %s\n", console.Grayf(k), v))
}
body, _ := io.ReadAll(r.body)
sb.WriteString(logger.StripSecrets(string(body)))
return sb.String()
}
34 changes: 34 additions & 0 deletions http/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package http

import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"

"github.com/flanksource/commons/console"
"github.com/flanksource/commons/logger"
)

// Response extends the stdlib http.Response type and extends its functionality
Expand Down Expand Up @@ -88,3 +92,33 @@ func (h *Response) IsJSON() bool {

return false
}

func (h *Response) HeaderMap() map[string]string {
headers := make(map[string]string)
for k, v := range h.Header {
headers[k] = strings.Join(v, ", ")
}
return headers
}

func (h *Response) Debug() string {
// mimic the response, + add content-type and size
var sb strings.Builder

if h.Request != nil {
sb.WriteString(h.Request.Debug())
}

sb.WriteString(fmt.Sprintf("\n====> Status: %d\n", h.StatusCode))
for k, v := range logger.StripSecretsFromMap(h.HeaderMap()) {
sb.WriteString(fmt.Sprintf(" %s: %s\n", console.Grayf(k), v))
}
if h.IsJSON() {
r, _ := h.AsJSON()
sb.WriteString(logger.Pretty(r))
} else {
body, _ := io.ReadAll(h.Body)
sb.WriteString(string(body))
}
return sb.String()
}
60 changes: 50 additions & 10 deletions logger/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"regexp"
"strings"

"github.com/flanksource/commons/properties"
"github.com/spf13/pflag"
)

Expand Down Expand Up @@ -33,7 +34,6 @@ func (f *flagSet) Parse() error {
logFlagset := pflag.NewFlagSet("logger", pflag.ContinueOnError)
// standalone parsing of flags to ensure we always have the correct values
f.bindFlags(logFlagset)
logFlagset.ParseErrorsWhitelist.UnknownFlags = true
if err := logFlagset.Parse(os.Args[1:]); err != nil {
return err
}
Expand Down Expand Up @@ -63,6 +63,40 @@ func IsJsonLogs() bool {
return flags.jsonLogs
}

type Flags struct {
Color, ReportCaller, JsonLogs, LogToStderr bool
Level string
LevelCount int
}

func Configure(flags Flags) {
// Get the verbosity count from the parsed flags
if flags.LevelCount > 0 {
currentLogger.SetLogLevel(flags.LevelCount)
} else if level := flags.Level; level != "" && level != "info" {
currentLogger.SetLogLevel(level)
}

// Apply other flags
if flags.JsonLogs {
properties.Set("log.json", "true")
}

if flags.ReportCaller {
properties.Set("log.report.caller", "true")
}

if flags.LogToStderr {
properties.Set("log.stderr", "true")
}
if !flags.Color {
properties.Set("log.color", "false")
}

currentLogger = *New("")

}

// BindFlags add flags to an existing flag set,
// note that this is not an actual binding which occurs later during initialization
func BindFlags(flags *pflag.FlagSet) {
Expand All @@ -83,7 +117,7 @@ func UseCobraFlags(flags *pflag.FlagSet) {
} else if level, err := flags.GetString("log-level"); err == nil && level != "" && level != "info" {
currentLogger.SetLogLevel(level)
}

// Apply other flags
if jsonLogs, err := flags.GetBool("json-logs"); err == nil && jsonLogs {
currentLogger = New("")
Expand All @@ -106,7 +140,8 @@ func Infof(format string, args ...interface{}) {
// It automatically redacts common secret patterns like passwords, tokens, and API keys.
//
// Example:
// logger.Secretf("Connecting with password=%s", password) // password will be redacted
//
// logger.Secretf("Connecting with password=%s", password) // password will be redacted
func Secretf(format string, args ...interface{}) {
currentLogger.Tracef(StripSecrets(fmt.Sprintf(format, args...)))
}
Expand All @@ -115,7 +150,8 @@ func Secretf(format string, args ...interface{}) {
// Useful for debugging complex data structures.
//
// Example:
// logger.Prettyf("User data:", userStruct) // Logs formatted struct
//
// logger.Prettyf("User data:", userStruct) // Logs formatted struct
func Prettyf(msg string, obj interface{}) {
currentLogger.Tracef(msg, Pretty(obj))
}
Expand Down Expand Up @@ -143,11 +179,13 @@ func Tracef(format string, args ...interface{}) {
func Fatalf(format string, args ...interface{}) {
currentLogger.Fatalf(format, args...)
}

// V returns a verbose logger for conditional logging at the specified level.
// The level can be an integer or a LogLevel constant.
//
// Example:
// logger.V(2).Infof("Detailed info") // Only logs at verbosity 2+
//
// logger.V(2).Infof("Detailed info") // Only logs at verbosity 2+
func V(level any) Verbose {
return currentLogger.V(level)
}
Expand All @@ -160,9 +198,10 @@ func IsTraceEnabled() bool {
// IsLevelEnabled returns true if the specified verbosity level is enabled.
//
// Example:
// if logger.IsLevelEnabled(3) {
// // Perform expensive operation only if logging at level 3
// }
//
// if logger.IsLevelEnabled(3) {
// // Perform expensive operation only if logging at level 3
// }
func IsLevelEnabled(level int) bool {
return currentLogger.V(level).Enabled()
}
Expand All @@ -176,8 +215,9 @@ func IsDebugEnabled() bool {
// These values will be included in all log messages from the returned logger.
//
// Example:
// userLogger := logger.WithValues("user_id", 123, "session", "abc")
// userLogger.Infof("User action") // Logs with user_id=123 session=abc
//
// userLogger := logger.WithValues("user_id", 123, "session", "abc")
// userLogger.Infof("User action") // Logs with user_id=123 session=abc
func WithValues(keysAndValues ...interface{}) Logger {
return currentLogger.WithValues(keysAndValues...)
}
Expand Down
13 changes: 10 additions & 3 deletions logger/slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ var SlogTraceLevel slog.Level = slog.LevelDebug - 1
var SlogFatal = slog.LevelError + 1

func GetSlogLogger() SlogLogger {
return currentLogger.(SlogLogger)
switch currentLogger := currentLogger.(type) {
case SlogLogger:
return currentLogger
case *SlogLogger:
return *currentLogger
}
return SlogLogger{}
}

func onPropertyUpdate(props *properties.Properties) {
Expand Down Expand Up @@ -167,8 +173,9 @@ func camelCaseWords(s string) []string {
// Multiple names create a hierarchical logger (e.g., GetLogger("app", "db") creates "app.db").
//
// Example:
// dbLogger := logger.GetLogger("database")
// apiLogger := logger.GetLogger("api", "v1")
//
// dbLogger := logger.GetLogger("database")
// apiLogger := logger.GetLogger("api", "v1")
func GetLogger(names ...string) *SlogLogger {
parent, _ := namedLoggers.Load(rootName)
if len(names) == 0 {
Expand Down
Loading