Files
Byron.wang ca3d00229e Feat/Implement structured logging and Trace ID propagation (#552)
* use slog instead of log package and format to new log schema

* update the environment name to LOG_OUTPUT_FORMAT

* add the env to .env.example

* fix log reference error

* change the order of milldlewares

* delete unused code

* fix the concurrently session potential race condition

* fix the log format in tests

* update the duplicate code

* refactor: convert log functions to slog structured format

- Change log.Error/Info/Warn/Debug/Panic to accept msg + key-value pairs
- Remove printf-style formatting from log functions
- Update log calls in internal/cluster, internal/db, internal/core/session_manager
- Remove unused 'initialized' variable from log package
- Remaining files will be updated in follow-up commits

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: update all log call sites to use slog structured format

Convert all log.Error, log.Info, log.Warn, log.Debug, and log.Panic
calls from printf-style formatting to slog key-value pairs.

Before: log.Error("failed to do something: %s", err.Error())
After:  log.Error("failed to do something", "error", err)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: update cmd/ log calls to use slog structured format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: implement GnetLogger for structured logging in gnet

* refactor: remove deprecated log visibility functions and related calls

* feat: enhance session management with trace and identity context propagation

* feat: implement serverless transaction handler and writer for plugin runtime

* refactor: rename context field to traceCtx in RealBackwardsInvocation

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Yeuoly <admin@srmxy.cn>
2025-12-30 11:00:48 +08:00

106 lines
2.8 KiB
Go

package plugin_entities
import (
"encoding/json"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/parser"
)
type PluginUniversalEvent struct {
SessionId string `json:"session_id"`
Event PluginEventType `json:"event"`
Data json.RawMessage `json:"data"`
}
// ParsePluginUniversalEvent parses bytes into struct contains basic info of a message
// it's the outermost layer of the protocol
// error_handler will be called when data is not standard or itself it's an error message
func ParsePluginUniversalEvent(
data []byte,
statusText string,
sessionHandler func(sessionId string, data []byte),
heartbeatHandler func(),
errorHandler func(err string),
logHandler func(logEvent PluginLogEvent),
) {
// handle event
event, err := parser.UnmarshalJsonBytes[PluginUniversalEvent](data)
if err != nil {
if len(data) > 1024 {
errorHandler(err.Error() + " status: " + statusText + " original response: " + string(data[:1024]) + "...")
} else {
errorHandler(err.Error() + " status: " + statusText + " original response: " + string(data))
}
return
}
sessionId := event.SessionId
switch event.Event {
case PLUGIN_EVENT_LOG:
if event.Event == PLUGIN_EVENT_LOG {
logEvent, err := parser.UnmarshalJsonBytes[PluginLogEvent](
event.Data,
)
if err != nil {
log.Error("unmarshal json failed", "error", err)
return
}
if logHandler != nil {
logHandler(logEvent)
}
}
case PLUGIN_EVENT_SESSION:
sessionHandler(sessionId, event.Data)
case PLUGIN_EVENT_ERROR:
errorHandler(string(event.Data))
case PLUGIN_EVENT_HEARTBEAT:
heartbeatHandler()
}
}
type PluginEventType string
const (
PLUGIN_EVENT_LOG PluginEventType = "log"
PLUGIN_EVENT_SESSION PluginEventType = "session"
PLUGIN_EVENT_ERROR PluginEventType = "error"
PLUGIN_EVENT_HEARTBEAT PluginEventType = "heartbeat"
)
type PluginLogEvent struct {
Level string `json:"level"`
Message string `json:"message"`
Timestamp float64 `json:"timestamp"`
}
type SessionMessage struct {
Type SESSION_MESSAGE_TYPE `json:"type" validate:"required"`
Data json.RawMessage `json:"data" validate:"required"`
}
type SESSION_MESSAGE_TYPE string
const (
SESSION_MESSAGE_TYPE_STREAM SESSION_MESSAGE_TYPE = "stream"
SESSION_MESSAGE_TYPE_END SESSION_MESSAGE_TYPE = "end"
SESSION_MESSAGE_TYPE_ERROR SESSION_MESSAGE_TYPE = "error"
SESSION_MESSAGE_TYPE_INVOKE SESSION_MESSAGE_TYPE = "invoke"
)
type ErrorResponse struct {
Message string `json:"message"`
ErrorType string `json:"error_type"`
Args map[string]any `json:"args" validate:"omitempty,max=10"` // max 10 args
}
func (e *ErrorResponse) Error() string {
return parser.MarshalJson(map[string]any{
"message": e.Message,
"error_type": e.ErrorType,
"args": e.Args,
})
}