Skip to content

Deploy #14

Merged
joaoarthurlm merged 9 commits intomainfrom
dev
Feb 11, 2026
Merged

Deploy #14
joaoarthurlm merged 9 commits intomainfrom
dev

Conversation

@joaoarthurlm
Copy link
Contributor

@gemini-code-assist
Copy link

Summary of Changes

Hello @joaoarthurlm, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the logging toolkit by introducing a modular, versioned approach. It separates the existing logging functionality into a dedicated V1 package and provides a new V2 implementation with a simplified file naming convention. A common utility for processing log content into JSON events has also been extracted, enhancing code reusability and maintainability. This change provides greater flexibility in choosing logging behaviors and streamlines the core logging interface.

Highlights

  • Refactored Core Logging Logic: The main mchlogcore package has been refactored to act as a facade, delegating logging operations to specific versioned implementations.
  • Introduced Versioned Logging: Two new packages, mchlogcorev1 and mchlogcorev2, were added to house distinct logging implementations. V1 retains the original logic with IP-based filenames and channel-based file handling, while V2 offers a simpler file structure without IP or timestamps in filenames and uses direct logger caching.
  • Centralized JSON Log Event Creation: A new mchlogcommon package was created to centralize the GetJSONLogger function, which is responsible for converting various content types (maps, slices, strings) into a zerolog.Event. This function is now utilized by both V1 and V2 logging implementations.
  • Configurable Logging Version: The mchlogcore package now includes a LogVersion type and a SetVersion function, allowing users to explicitly choose which logging implementation (V1 or V2) to use at runtime.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • mchlogcommon/mchlogcommon.go
    • Added a new package containing the GetJSONLogger function to standardize JSON log event creation.
  • mchlogcore/mchlog.go
    • Refactored the main logging logic into a facade that delegates to mchlogcorev1 or mchlogcorev2 based on a configurable version.
    • Removed the original logging implementation details, replacing them with calls to the new versioned packages.
    • Introduced LogVersion type and SetVersion function for dynamic logger selection.
  • mchlogcorev1/mchlogv1.go
    • Added a new package containing the original mchlogcore logging implementation, including IP in filenames and a channel-based file handling mechanism.
  • mchlogcorev2/mchlogv2.go
    • Added a new package containing a new logging implementation with a simpler file naming convention (no IP or timestamp in filenames) and direct logger caching.
Activity
  • No human activity (comments, reviews) was recorded in the provided context for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@joaoarthurlm joaoarthurlm merged commit eb94647 into main Feb 11, 2026
1 check passed
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the logging toolkit into V1 and V2 implementations, moving towards a more modular design with a new mchlogcommon package. However, it introduces several critical security vulnerabilities: both versions are susceptible to Path Traversal via the subject parameter, V1 uses insecure world-writable file and directory permissions, and V2 has a race condition leading to file descriptor leaks during concurrent logger initialization. Additionally, there are general issues concerning error handling, type conversion logic, and overall code clarity that need attention to ensure robustness and maintainability.

Comment on lines +48 to +63
var arr []any
if arr, ok = content.([]any); !ok {
tam := v.Len()
for i := 0; i < tam; i++ {
va := v.Index(i)
switch va.Kind() {
case reflect.String:
arr = append(arr, va.String())
case reflect.Int, reflect.Int64, reflect.Int32:
arr = append(arr, va.Int())
case reflect.Float64, reflect.Float32:
arr = append(arr, va.Float())
}
}
}
event = logger.Log().Fields(arr)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current logic for handling reflect.Slice or reflect.Array types (when not []byte or []any interpreted as key-value pairs) can lead to incorrect JSON output. If content is a strongly typed slice (e.g., []int{1, 2, 3}), it is converted to []any{1, 2, 3}. Passing this to logger.Log().Fields(arr) will cause zerolog to misinterpret the array elements as alternating keys and values, which is likely not the intended behavior and can result in malformed log entries.

To ensure correct JSON formatting, consider marshaling such slices/arrays to JSON and then unmarshaling them into a map[string]any if they represent a JSON object, or logging them as a single array field if they represent a JSON array of primitives. If the content cannot be converted to a valid JSON object, it should be explicitly treated as an invalid type.

Suggested change
var arr []any
if arr, ok = content.([]any); !ok {
tam := v.Len()
for i := 0; i < tam; i++ {
va := v.Index(i)
switch va.Kind() {
case reflect.String:
arr = append(arr, va.String())
case reflect.Int, reflect.Int64, reflect.Int32:
arr = append(arr, va.Int())
case reflect.Float64, reflect.Float32:
arr = append(arr, va.Float())
}
}
}
event = logger.Log().Fields(arr)
var arr []any
if arr, ok = content.([]any); ok { // If content is already []any, assume it's key-value pairs
event = logger.Log().Fields(arr)
} else {
// For other slice/array types (e.g., []int, []string), try to marshal to JSON
// and then unmarshal to a map. If it's a simple array of primitives,
// unmarshaling to a map will fail, correctly triggering the error.
var tempBytes []byte
tempBytes, err = json.Marshal(content)
if err == nil {
var m map[string]any
if err = json.Unmarshal(tempBytes, &m); err == nil {
event = logger.Log().Fields(m)
}
}
}

Comment on lines +123 to +126
if err != nil {
log.Error().Err(err).Interface("conteudo", subject).Msg("mchlogcorev2: falha ao processar conteúdo do log")
return
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the file opening error, if mchlogcommon.GetJSONLogger returns an error, it is logged internally, but the LogSubject method returns silently. This means that if the content cannot be properly formatted into a JSON log event, the log entry is effectively dropped without the caller being informed. This can lead to critical information loss.

Evaluate whether silent failure is acceptable for this logging toolkit, or if a more explicit error handling strategy is needed to ensure log integrity.

Comment on lines +98 to +118
if obj, ok := l.mapLogger.Load(subject); ok {
logger = obj.(*zerolog.Logger)
} else {
// Se não estiver no cache, abre o arquivo e inicializa o logger
filename := filepath.Join(l.path, subject, subject+ccLogFileSuffix)

// Garante que a árvore de diretórios existe
_ = os.MkdirAll(filepath.Dir(filename), 0755)

// Abre o arquivo para escrita ao final (append). Cria se não existir.
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Error().Err(err).Str("filename", filename).Interface("conteudo", subject).Msg("mchlogcorev2: falha ao criar e abrir arquivo de log")
return
}

// Importante: Não fechamos o arquivo (f.Close) aqui pois ele será
// gerenciado pelo objeto logger que ficará no cache durante a vida do processo.
newLogger := zerolog.New(f).With().Timestamp().Logger()
logger = &newLogger
l.mapLogger.Store(subject, logger)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

This block contains a race condition where multiple goroutines logging to a new subject simultaneously can lead to file descriptor leaks. Only the last one to call Store will have its logger cached, while others will leak their file descriptors. Additionally, the LogSubject method silently handles os.OpenFile errors, which can result in lost log data without the application being aware of the failure. To address these, use a sync.Mutex or sync.Map.LoadOrStore with a proper initialization pattern to ensure thread-safe logger creation, and consider a mechanism to notify callers of critical logging failures.

ip = "-" + ip
}

logFile := filepath.Join(l.path, subject, subject+ip+"-"+dataHora+ccLogFileSuffix)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The subject parameter is used to construct the log file path without sanitization. An attacker who can control the subject string can use path traversal sequences (e.g., ..) to create directories and log files outside the intended base directory. It is recommended to sanitize the subject by using filepath.Base(subject) or by validating it against an allow-list.

logger = obj.(*zerolog.Logger)
} else {
// Se não estiver no cache, abre o arquivo e inicializa o logger
filename := filepath.Join(l.path, subject, subject+ccLogFileSuffix)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The subject parameter is used to construct the log file path without sanitization, similar to the V1 implementation. This allows for path traversal attacks if the subject is derived from untrusted input. Consider sanitizing the input to ensure it does not contain path separators or traversal sequences.

var err error
dirName := filepath.Dir(flt.filename)
if _, err = os.Stat(dirName); os.IsNotExist(err) {
err = os.MkdirAll(dirName, os.ModePerm)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Directories are created with os.ModePerm (0777), which is insecure as it allows any user on the system to create, delete, or modify files within the log directory. Use more restrictive permissions like 0750.

Comment on lines +71 to +72
default:
err = errors.New("")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The default error message errors.New("") is not descriptive and makes debugging difficult. It's important to provide clear and informative error messages to help identify the root cause of issues.

Please provide a more specific error message that indicates the type of content that was not handled.

Suggested change
default:
err = errors.New("")
default:
err = fmt.Errorf("unsupported reflect kind: %s", v.Kind().String())

Comment on lines +15 to +50
/* Exemplo de uso do mchlogcore.
// Primeiro o pacote precisa ser inicializado com o
// caminho a gravar os arquivos de log:
mchlogcore.InitializeMchLog("./etc/log")
// O pacote mchlogcore contém o objeto público MchLog e
// o único método acessível: LogSubject()
// Origem do log é uma string ou []byte em formato de json (tem que ser um json válido)
m := fmt.Sprintf("{\"somekey\":{\"id\":\"123\"},\"tick\":%d,\"time\":\"2020-11-09T12:05:19+01:00\",\"testmsg\":\"some msg\"}", 10)
mchlogcore.MchLog.LogSubject("teste", m, nil)
ou
mchlogcore.MchLog.LogSubject("teste", []byte(m), nil)
// output: {"somekey":{"id":"123"},"testmsg":"some msg","tick":10,"time":"2020-11-09T12:05:19+01:00","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste-169.254.215.202-2022101222.log
// Origem do log é um mapa onde a chave é uma string e o tipo do valor "dinâmico" (interface{}).
// A chave TEM que ser string, o valor pode ser string, int ou float.
// O tipo do valor pode ser estático também (string, por exemplo).
h := make(map[string]any)
h["mapa1"] = "aaa"
h["mapa2"] = 22
mchlogcore.MchLog.LogSubject("teste", h, nil)
// output: {"mapa1":"aaa","mapa2":22,"data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste-169.254.215.202-2022101222.log
// Origem do log é um array/slice de tipo dinâmico.
// O tipo do array/slice pode ser estático, nesse caso só pode ser string.
// Começando do indice zero, os elementos pares são as "chaves" e
// os ímpares os valores, que podem ser string, int ou float.
var a []any
a = append(a, "key_array1", 22, "key_array2")
a = append(a, "55")
mchlogcore.MchLog.LogSubject("teste", a, nil)
// output: {"key_array1":22,"key_array2":"55","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste-169.254.215.202-2022101222.log
// Usando o log do exemplo anterior, desta vez enviando um erro.
mchlogcore.MchLog.LogSubject("teste", a, errors.New("isto é um erro forçado"))
// output: {"key_array1":22,"key_array2":"55","error":"isto é um erro forçado","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/err_teste/err_teste-169.254.215.202-2022101222.log

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The extensive commented-out example code, while informative, adds significant verbosity to the file. This can make the actual code harder to read and maintain. Consider moving such detailed examples to a separate documentation file (e.g., a README.md in the package directory) or a dedicated _example directory, keeping the code files cleaner and focused on implementation.

Comment on lines +15 to +49
// Primeiro o pacote precisa ser inicializado com o
// caminho a gravar os arquivos de log:
mchlogcore.InitializeMchLog("./etc/log")
// O pacote mchlogcore contém o objeto público MchLog e
// o único método acessível: LogSubject()
// Origem do log é uma string ou []byte em formato de json (tem que ser um json válido)
m := fmt.Sprintf("{\"somekey\":{\"id\":\"123\"},\"tick\":%d,\"time\":\"2020-11-09T12:05:19+01:00\",\"testmsg\":\"some msg\"}", 10)
mchlogcore.MchLog.LogSubject("teste", m, nil)
ou
mchlogcore.MchLog.LogSubject("teste", []byte(m), nil)
// output: {"somekey":{"id":"123"},"testmsg":"some msg","tick":10,"time":"2020-11-09T12:05:19+01:00","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste.log
// Origem do log é um mapa onde a chave é uma string e o tipo do valor "dinâmico" (interface{}).
// A chave TEM que ser string, o valor pode ser string, int ou float.
// O tipo do valor pode ser estático também (string, por exemplo).
h := make(map[string]any)
h["mapa1"] = "aaa"
h["mapa2"] = 22
mchlogcore.MchLog.LogSubject("teste", h, nil)
// output: {"mapa1":"aaa","mapa2":22,"data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste.log
// Origem do log é um array/slice de tipo dinâmico.
// O tipo do array/slice pode ser estático, nesse caso só pode ser string.
// Começando do indice zero, os elementos pares são as "chaves" e
// os ímpares os valores, que podem ser string, int ou float.
var a []any
a = append(a, "key_array1", 22, "key_array2")
a = append(a, "55")
mchlogcore.MchLog.LogSubject("teste", a, nil)
// output: {"key_array1":22,"key_array2":"55","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/teste/teste.log
// Usando o log do exemplo anterior, desta vez enviando um erro.
mchlogcore.MchLog.LogSubject("teste", a, errors.New("isto é um erro forçado"))
// output: {"key_array1":22,"key_array2":"55","error":"isto é um erro forçado","data_hora":"2022-10-12 19:11:33 UTC"}
// no arquivo ./etc/log/err_teste/err_teste.log

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to mchlogv1.go, the extensive commented-out example code here adds significant verbosity. Consider moving these detailed examples to external documentation or an _example directory to improve code readability and maintainability.

Comment on lines +114 to +118
// Importante: Não fechamos o arquivo (f.Close) aqui pois ele será
// gerenciado pelo objeto logger que ficará no cache durante a vida do processo.
newLogger := zerolog.New(f).With().Timestamp().Logger()
logger = &newLogger
l.mapLogger.Store(subject, logger)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment states that files are not closed because they are managed by the logger object in the cache for the lifetime of the process. While this can be efficient for a fixed and small number of log subjects, it can lead to an excessive number of open file descriptors if the subject parameter is dynamic and many unique subjects are logged over time. This could potentially exhaust system resources.

Consider adding a mechanism to explicitly close log files (e.g., a Close() method for the LogType or a cleanup routine) or implementing a strategy to periodically close and reopen less frequently used log files, especially if the number of unique subjects is not bounded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants