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

102 lines
3.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package server
import (
"errors"
"time"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache/helper"
"github.com/gin-gonic/gin"
"github.com/langgenius/dify-plugin-daemon/internal/db"
"github.com/langgenius/dify-plugin-daemon/internal/service"
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
"github.com/langgenius/dify-plugin-daemon/internal/types/exception"
"github.com/langgenius/dify-plugin-daemon/internal/types/models"
"github.com/langgenius/dify-plugin-daemon/pkg/entities/endpoint_entities"
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
)
// DifyPlugin supports register and use endpoint to improve the plugin's functionality
// you can use it to do some magics, looking forward to your imagination, Ciallo(∠·ω< )⌒
// - Yeuoly
// EndpointHandler is a function type that can be used to handle endpoint requests
type EndpointHandler func(ctx *gin.Context, hookId string, maxExecutionTime time.Duration, path string)
func (app *App) Endpoint(config *app.Config) func(c *gin.Context) {
return func(c *gin.Context) {
hookId := c.Param("hook_id")
path := c.Param("path")
// set X-Original-Host
if c.Request.Header.Get(endpoint_entities.HeaderXOriginalHost) == "" {
c.Request.Header.Set(endpoint_entities.HeaderXOriginalHost, c.Request.Host)
}
if app.endpointHandler != nil {
app.endpointHandler(c, hookId, time.Duration(config.PluginMaxExecutionTimeout)*time.Second, path)
} else {
app.EndpointHandler(c, hookId, time.Duration(config.PluginMaxExecutionTimeout)*time.Second, path)
}
}
}
func (app *App) EndpointHandler(ctx *gin.Context, hookId string, maxExecutionTime time.Duration, path string) {
endpointCacheKey := helper.EndpointCacheKey(hookId)
endpoint, err := cache.AutoGetWithGetter[models.Endpoint](
endpointCacheKey,
func() (*models.Endpoint, error) {
v, err := db.GetOne[models.Endpoint](
db.Equal("hook_id", hookId),
)
return &v, err
})
if errors.Is(err, db.ErrDatabaseNotFound) {
ctx.JSON(404, exception.BadRequestError(errors.New("endpoint not found")).ToResponse())
return
}
if err != nil {
log.Error("get endpoint error", "error", err)
ctx.JSON(500, exception.InternalServerError(errors.New("internal server error")).ToResponse())
return
}
// get plugin installation
pluginInstallationCacheKey := helper.PluginInstallationCacheKey(endpoint.PluginID, endpoint.TenantID)
pluginInstallation, err := cache.AutoGetWithGetter[models.PluginInstallation](
pluginInstallationCacheKey,
func() (*models.PluginInstallation, error) {
v, err := db.GetOne[models.PluginInstallation](
db.Equal("plugin_id", endpoint.PluginID),
db.Equal("tenant_id", endpoint.TenantID),
)
return &v, err
},
)
if err != nil {
ctx.JSON(404, exception.BadRequestError(errors.New("plugin installation not found")).ToResponse())
return
}
pluginUniqueIdentifier, err := plugin_entities.NewPluginUniqueIdentifier(
pluginInstallation.PluginUniqueIdentifier,
)
if err != nil {
ctx.JSON(400, exception.UniqueIdentifierError(
errors.New("invalid plugin unique identifier"),
).ToResponse())
return
}
// check if plugin exists in current node
if needRedirecting, originalError := app.pluginManager.NeedRedirecting(pluginUniqueIdentifier); needRedirecting {
app.redirectPluginInvokeByPluginIdentifier(ctx, pluginUniqueIdentifier, originalError)
} else {
service.Endpoint(ctx, endpoint, pluginInstallation, maxExecutionTime, path)
}
}