meshery / meshery/meshkit

Logger never reads errors.Severity to choose a level - Error() always emits at ErrorLevel

Open
#1,083 3 comments 0 reactions 1 assignee View on GitHub

@Om-Beast is already working on this.

Since Aug 4, 2026.

Dominant language
Go
Stars
223
Forks
225
Avg merge
7d 1h
Merged PRs (30d)
1

Description

## Summary

`errors.Severity` is carried on every structured error and surfaced by `errors.GetSeverity`, but no logger method ever reads it to choose a log level. Each method hardcodes one level and attaches the severity as a *field* instead:

`logger/logger.go` (v1.0.10):

```go
func (l *Logger) Error(err error) {
if err == nil {
return
}

l.errorHandler.WithFields(logrus.Fields{
"code": errors.GetCode(err),
"severity": errors.GetSeverity(err), // <- recorded
...
}).Log(logrus.ErrorLevel, err.Error()) // <- but the level is fixed
}
```

`Warn` has the same shape at `logrus.WarnLevel`, and `Fatal` at `logrus.FatalLevel`.

So severity is descriptive metadata inside a line whose level was already decided by which method the caller picked. The two are free to disagree, and nothing detects it.

## Why it matters

`errors/types.go` defines a graded model - `Emergency`, `None`, `Alert`, `Critical`, `Fatal` - which reads as an instruction about how loud an error is. An author who deliberately constructs an error at `None` to mean "this is routine, it must not be able to manufacture alerts" gets no such behaviour: `Logger.Error` still emits it at `ErrorLevel`.

The practical consequence is that any error path reachable by unauthenticated or low-privilege callers becomes a way to generate error-level log volume on demand, even when the author graded it as routine. That is exactly the signal the same logs are used to alert on.

The gap is invisible at the call site. `log.Error(someNoneSeverityError)` looks correct and compiles, and the emitted line even carries `severity=None` next to `level=error`.

## Evidence of consumers working around it

`layer5io/meshery-cloud` hand-rolls the split rather than getting it from meshkit:

- `server/handlers/sessions.go` - `logRefRejection` classifies a rejection and picks Warn vs Debug itself. That repo's `AGENTS.md` states the level split as a hard contract, which is a signal the behaviour is wanted and is currently the consumer's job.
- A second call site under `server/handlers/academy/` is adding the same local split now, for the same reason.

Two independent hand-rolled implementations of one policy in a single consumer suggests the policy belongs in the logger.

## Suggested shape

Either of these resolves it; the first is source-compatible:

1. Have `Logger.Error` derive its level from `errors.GetSeverity(err)` and fall back to `ErrorLevel` when the severity is absent or unset. Callers keep writing `log.Error(err)` and get the level the error was graded with.
2. Add an explicit severity-aware entry point (for example `Logger.Log(err)`) that maps severity to level, and document `Error`/`Warn` as the fixed-level forms.

Whichever is chosen, the mapping should be stated in the package docs, since today a reader can reasonably assume either behaviour. Note that `Emergency = iota` makes `Emergency` the zero value, so an error constructed without an explicit severity currently reports the *most* severe grade - worth settling as part of the same change, because a naive severity-to-level mapping would turn every unset severity into the loudest level.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.