Discordrus is a Logrus hook package that enables you to send log entries to Discord channels via webhooks. This package is specifically designed for HTTP request logging with asynchronous delivery features that don't block your application.
This package was developed to meet the monitoring and debugging needs of Go applications, particularly for:
- HTTP Request Monitoring: Record detailed HTTP request information for failed or error requests
- Real-time Alerting: Get instant notifications in Discord when errors occur
- Production Debugging: Simplify debugging with comprehensive request details
- Non-blocking Logging: Webhook delivery is performed asynchronously without hampering application performance
- ✅ Asynchronous Delivery - Webhooks sent in separate goroutines
- ✅ HTTP Request Logging - Support for detailed HTTP request logging (method, URL, body, headers)
- ✅ Multiple Content Types - Support for JSON, form-data, multipart, and raw body
- ✅ File Attachments - Long log messages sent as file attachments
- ✅ Customizable Log Levels - Configure which log levels to send
- ✅ Rich Discord Embeds - Clean message formatting with color-coded levels
- ✅ Error Handling - Graceful handling for various error scenarios
To use this package in your Go project:
go get github.com/murbagus/discordruspackage main
import (
"github.com/sirupsen/logrus"
"github.com/murbagus/discordrus"
)
func main() {
// Initialize logger
logger := logrus.New()
// Create Discord webhook hook
hook := discordrus.NewHook("https://discord.com/api/webhooks/YOUR_WEBHOOK_URL")
// Add hook to logger
logger.AddHook(hook)
// Send log to Discord
logger.Error("Something went wrong in the application!")
}// Only send Error and Fatal levels to Discord
hook := discordrus.NewHook(
"https://discord.com/api/webhooks/YOUR_WEBHOOK_URL",
logrus.ErrorLevel,
logrus.FatalLevel,
)This package provides a special field for logging HTTP requests with comprehensive details:
func handleAPICall(w http.ResponseWriter, r *http.Request) {
// Example HTTP request to be logged
apiRequest, err := http.NewRequest("POST", "https://api.example.com/users",
strings.NewReader(`{"name": "John Doe", "email": "[email protected]"}`))
if err != nil {
logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: apiRequest,
}).Error("Failed to create API request")
return
}
apiRequest.Header.Set("Content-Type", "application/json")
// Execute request
client := &http.Client{}
resp, err := client.Do(apiRequest)
if err != nil {
logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: apiRequest,
}).Error("API call failed")
return
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: apiRequest,
}).Errorf("API returned error status: %d", resp.StatusCode)
}
}If you don't have an *http.Request object, you can fill in the data manually:
logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Method: "POST",
URL: "https://api.example.com/users",
BodyString: `{"name": "John Doe", "email": "[email protected]"}`,
Headers: "Content-Type: application/json\nAuthorization: Bearer token123",
}).Error("User creation failed")This package can handle various HTTP content types:
// JSON body will be displayed with proper formatting
request.Header.Set("Content-Type", "application/json")// Form data will be parsed and displayed as key-value pairs
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")// Multipart forms will display field data and file information
request.Header.Set("Content-Type", "multipart/form-data")// Other content types will be displayed as raw body (max 1KB)Logs will be sent to Discord with structured embed formatting:
- 🔴 Error/Fatal/Panic: Red
- 🟡 Warning: Yellow
- 🔵 Info/Debug: Blue
- Level & Timestamp: Shows log level and time
- Error Message: Error details if present
- Request Payload: HTTP request details (method, URL, body, headers)
- Log Message: Main log message (as file if too long)
For automatic logging on all HTTP requests:
func LoggingMiddleware(logger *logrus.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Clone request for logging
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
// Wrapper to capture response status
wrapper := &responseWrapper{ResponseWriter: w, statusCode: 200}
// Process request
next.ServeHTTP(wrapper, r)
// Log if error occurred
if wrapper.statusCode >= 400 {
// Restore body for logging
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: r,
}).Errorf("HTTP %d: %s %s", wrapper.statusCode, r.Method, r.URL.Path)
}
})
}
}
type responseWrapper struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWrapper) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}Add error context for more detailed information:
logger.WithFields(logrus.Fields{
discordrus.REQUEST_FIELD_KEY: discordrus.LoggerHttpRequestPayload{
Request: request,
},
"error": err,
"user_id": userID,
"operation": "create_user",
}).Error("Database operation failed")Here's a complete example of usage in a web application:
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
"github.com/sirupsen/logrus"
"github.com/murbagus/discordrus"
)
type Server struct {
logger *logrus.Logger
}
func main() {
// Setup logger
logger := logrus.New()
logger.SetLevel(logrus.DebugLevel)
// Setup Discord hook
webhookURL := os.Getenv("DISCORD_WEBHOOK_URL")
if webhookURL != "" {
hook := discordrus.NewHook(webhookURL)
logger.AddHook(hook)
}
server := &Server{logger: logger}
// Routes
http.HandleFunc("/api/users", server.createUser)
logger.Info("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
logger.Fatal("Server failed to start:", err)
}
}
func (s *Server) createUser(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Read request body
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
s.logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: r,
}).Error("Failed to read request body")
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
// Restore body for potential re-reading
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
// Parse JSON
var user struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := json.Unmarshal(bodyBytes, &user); err != nil {
s.logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: r,
}).Error("Invalid JSON payload")
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Simulate database operation
if user.Email == "" {
s.logger.WithField(discordrus.REQUEST_FIELD_KEY, discordrus.LoggerHttpRequestPayload{
Request: r,
}).Error("Email is required")
http.Error(w, "Email is required", http.StatusBadRequest)
return
}
// Success response
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "success",
"message": "User created successfully",
})
s.logger.WithField("user_email", user.Email).Info("User created successfully")
}- Go 1.19 or higher
- Discord server with configured webhook
- Open your Discord server
- Go to Server Settings → Integrations → Webhooks
- Click "New Webhook"
- Select target channel and copy webhook URL
- Use that URL in your code
# Clone repository
git clone https://github.com/murbagus/discordrus.git
cd discordrus
# Install dependencies
go mod tidy
# Set environment variable
export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
# Run tests
go test -vThis package is released under MIT License.
Contributions are very welcome! Please:
- Fork this repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you find bugs or have feature requests, please create a new issue.
For questions or help:
- Open GitHub Issues
- Email: [[email protected]]
Happy Logging! 🚀