Logging Security
Overview
To protect sensitive information from being leaked into logs, dify-spring-boot-starter provides comprehensive log masking functionality. This feature is enabled by default and can automatically identify and mask sensitive data, including API Keys, Tokens, passwords, Cookies, etc.
Additionally, the framework provides security protection for SSE (Server-Sent Events) streaming responses to avoid blocking issues caused by log recording.
Features
🔒 Sensitive Information Masking
Automatically masks the following types of sensitive information:
HTTP Headers
AuthorizationCookieTokenAPI-Key/ApiKey- Other headers containing sensitive keywords
URL Parameters
api_key/apikeytokenauthorizationpasswordsecretaccess_token/accessTokenrefresh_token/refreshToken
Cookies
- All cookies are displayed as
***MASKED***in logs
- All cookies are displayed as
Request/Response Body
- Uses intelligent MaskingEngine to identify and mask sensitive fields
- Supports multiple formats including JSON, form data, etc.
🚀 SSE Streaming Response Protection
- Automatically detects
Content-Type: text/event-streamresponses - SSE responses only log headers, not body
- Avoids blocking streaming consumption due to log recording
- Prevents memory overflow and connection timeout
Configuration
Spring Boot Configuration
dify:
client-config:
logging: true # Enable logging, default true
logging-mask-enabled: true # Enable log masking, default true
Pure Java Configuration
import io.github.guoshiqiufeng.dify.core.config.DifyProperties;
import io.github.guoshiqiufeng.dify.client.integration.okhttp.http.JavaHttpClientFactory;
import io.github.guoshiqiufeng.dify.client.codec.jackson.JacksonJsonMapper;
// Create client configuration
DifyProperties.ClientConfig clientConfig = new DifyProperties.ClientConfig();
clientConfig.setLogging(true); // Enable logging
clientConfig.setLoggingMaskEnabled(true); // Enable masking (default)
// Create HTTP client factory
JavaHttpClientFactory factory = new JavaHttpClientFactory(new JacksonJsonMapper());
// Create client
DifyChatClient client = DifyChatBuilder.builder()
.baseUrl("https://api.dify.ai")
.clientConfig(clientConfig)
.httpClientFactory(factory)
.build();
Configuration Parameters
logging
- Type:
Boolean - Default:
true - Description: Whether to enable logging. Must be used with
DEBUGlog level.
loggingMaskEnabled
- Type:
Boolean - Default:
true - Description: Whether to enable log masking. Recommended to keep enabled in production to protect sensitive information.
Usage Examples
Example 1: Default Configuration (Recommended)
dify:
url: https://api.dify.ai
client-config:
logging: true
logging-mask-enabled: true # Enabled by default, can be omitted
Log Output Example:
【Dify】Request: POST https://api.dify.ai/v1/chat-messages?api_key=***MASKED***
【Dify】Request Headers: {Authorization: ***MASKED***, Content-Type: application/json}
【Dify】Request Body: {"query":"Hello","user":"user-123"}
【Dify】Response: 200 OK (123ms)
【Dify】Response Headers: {Content-Type: application/json}
【Dify】Response Body: {"answer":"Hello! How can I help you?"}
Example 2: Debug Mode (Masking Disabled)
dify:
url: https://api.dify.ai
client-config:
logging: true
logging-mask-enabled: false # Disable masking for debugging
Log Output Example:
【Dify】Request: POST https://api.dify.ai/v1/chat-messages?api_key=app-abc123def456
【Dify】Request Headers: {Authorization: Bearer app-abc123def456, Content-Type: application/json}
【Dify】Request Body: {"query":"Hello","user":"user-123"}
【Dify】Response: 200 OK (123ms)
【Dify】Response Headers: {Content-Type: application/json}
【Dify】Response Body: {"answer":"Hello! How can I help you?"}
Example 3: SSE Streaming Response
dify:
url: https://api.dify.ai
client-config:
logging: true
logging-mask-enabled: true
Log Output Example:
【Dify】Request: POST https://api.dify.ai/v1/chat-messages
【Dify】Request Headers: {Authorization: ***MASKED***, Content-Type: application/json}
【Dify】Request Body: {"query":"Hello","response_mode":"streaming","user":"user-123"}
【Dify】Response: 200 OK (45ms)
【Dify】Response Headers: {Content-Type: text/event-stream}
【Dify】SSE response detected, skipping body logging to avoid blocking stream
Log Level Configuration
To enable log output, configure the log level to DEBUG:
Logback Configuration (Spring Boot Default)
<configuration>
<logger name="io.github.guoshiqiufeng.dify.client" level="DEBUG"/>
</configuration>
application.yml Configuration
logging:
level:
io.github.guoshiqiufeng.dify.client: DEBUG
Log4j2 Configuration
<Configuration>
<Loggers>
<Logger name="io.github.guoshiqiufeng.dify.client" level="DEBUG"/>
</Loggers>
</Configuration>
Security Best Practices
1. Production Environment Configuration
dify:
client-config:
logging: true # Enable logging for troubleshooting
logging-mask-enabled: true # Must enable masking to protect sensitive info
2. Development Environment Configuration
dify:
client-config:
logging: true
logging-mask-enabled: false # Can disable masking for debugging
3. Log Level Recommendations
- Production:
INFOorWARN(no detailed logs) - Testing:
DEBUG(detailed logs with masking enabled) - Development:
DEBUG(detailed logs, masking optional)
4. Sensitive Information Handling
- ✅ Recommended: Use environment variables or configuration center for API Keys
- ✅ Recommended: Enable log masking
- ✅ Recommended: Regularly review log output to ensure no sensitive information leakage
- ❌ Avoid: Hard-coding API Keys in code
- ❌ Avoid: Committing log files with sensitive information to version control
Technical Implementation
Masking Mechanism
The framework uses MaskingEngine for intelligent masking:
- Header Masking: Detects header names, masks if matching sensitive keywords
- URL Parameter Masking: Uses regex to match sensitive parameter names
- Body Masking: Uses lightweight Tokenizer to parse JSON/form data and identify sensitive fields
SSE Detection
Determines if response is SSE by checking Content-Type header:
private boolean isSseResponse(MediaType contentType) {
if (contentType == null) return false;
return "text".equals(contentType.type())
&& "event-stream".equals(contentType.subtype());
}
Performance Impact
- Log masking only executes at
DEBUGlevel - In production with
INFOlevel, masking logic is not executed - Masking operations use efficient regex and string operations, negligible performance impact
FAQ
Q1: How to temporarily disable masking for debugging?
A: Set logging-mask-enabled: false in configuration:
dify:
client-config:
logging-mask-enabled: false
Q2: Does masking affect actual API calls?
A: No. Masking only affects log output and does not modify actual request and response data.
Q3: How to customize masking rules?
A: Current version uses built-in masking rules. For customization, use LogMaskingUtils.createEngine() to create a custom MaskingEngine:
import io.github.guoshiqiufeng.dify.core.logging.masking.MaskingConfig;
import io.github.guoshiqiufeng.dify.core.logging.masking.MaskingEngine;
MaskingConfig config = MaskingConfig.builder()
.addSensitiveField("custom_field")
.maxBodyLength(1000)
.build();
MaskingEngine engine = LogMaskingUtils.createEngine(config);
Q4: Why doesn't SSE response log the body?
A: SSE is a streaming response. Reading the body would block stream consumption, causing:
- Client cannot receive data in real-time
- Potential memory overflow (large streaming responses)
- Connection timeout
Therefore, SSE responses only log headers, not body.
Q5: How to verify masking is working?
A:
- Configure log level to
DEBUG - Enable
logging-mask-enabled: true - Make a request with sensitive information
- Check log output, sensitive info should show as
***MASKED***
Version History
- v2.1.0: Enhanced log masking, added SSE safety protection
- v2.0.0: Introduced MaskingEngine with intelligent masking
- v1.x: Basic logging functionality