Skip to content

Commit 1d4b0b8

Browse files
committed
Review suggestions
- adding . at the end - reqId explanation - 'size' renaming in log for clarity - MDc examplee - "object creation" with example instead of vague computaiton - remove mention of "placeholder" warnings
1 parent 598f64b commit 1d4b0b8

File tree

11 files changed

+70
-20
lines changed

11 files changed

+70
-20
lines changed

core/src/main/java/com/sap/ai/sdk/core/AiCoreServiceKeyAccessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public List<ServiceBinding> getServiceBindings() throws ServiceBindingAccessExce
5252
throw new ServiceBindingAccessException("Failed to load service key from environment", e);
5353
}
5454
if (serviceKey == null) {
55-
log.debug("No service key found in environment variable {}", ENV_VAR_KEY);
55+
log.debug("No service key found in environment variable {}.", ENV_VAR_KEY);
5656
return List.of();
5757
}
5858
final String logMessage =

core/src/main/java/com/sap/ai/sdk/core/DeploymentResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void reloadDeployments(@Nonnull final String resourceGroup) {
4646
try {
4747
val apiClient = new DeploymentApi(service);
4848
val deployments = new HashSet<>(apiClient.query(resourceGroup).getResources());
49-
log.info("Found {} deployments in resource group '{}'", deployments.size(), resourceGroup);
49+
log.info("Found {} deployments in resource group '{}'.", deployments.size(), resourceGroup);
5050
cache.put(resourceGroup, deployments);
5151
} catch (final RuntimeException e) {
5252
throw new DeploymentResolutionException(

core/src/main/java/com/sap/ai/sdk/core/common/ClientResponseHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private T parseSuccess(@Nonnull final ClassicHttpResponse response) throws E {
9191
try {
9292
return objectMapper.readValue(content, successType);
9393
} catch (final JsonProcessingException e) {
94-
log.error("Failed to parse response to type {}", successType);
94+
log.error("Failed to parse response to type {}.", successType);
9595
throw exceptionFactory.build("Failed to parse response", e).setHttpResponse(response);
9696
}
9797
}

core/src/main/java/com/sap/ai/sdk/core/common/ClientStreamingHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public Stream<D> handleStreamingResponse(@Nonnull final ClassicHttpResponse resp
8585
}
8686
return objectMapper.treeToValue(jsonNode, successType);
8787
} catch (final IOException e) {
88-
log.error("Failed to parse delta chunk to type {}", successType);
88+
log.error("Failed to parse delta chunk to type {}.", successType);
8989
final String message = "Failed to parse delta chunk";
9090
throw exceptionFactory.build(message, e).setHttpResponse(response);
9191
}

core/src/main/java/com/sap/ai/sdk/core/common/IterableStreamConverter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class IterableStreamConverter<T> implements Iterator<T> {
3535
static final int BUFFER_SIZE = 8192;
3636

3737
private static final String ERR_CONTENT = "Failed to read response content.";
38-
private static final String ERR_INTERRUPTED = "Parsing response content was interrupted";
39-
private static final String ERR_CLOSE = "Could not close input stream with error: {} (ignored)";
38+
private static final String ERR_INTERRUPTED = "Parsing response content was interrupted.";
39+
private static final String ERR_CLOSE = "Could not close input stream with error: {} (ignored).";
4040

4141
/** Read next entry for Stream or {@code null} when no further entry can be read. */
4242
private final Callable<T> readHandler;
@@ -70,7 +70,7 @@ public boolean hasNext() {
7070
} catch (final Exception e) {
7171
isDone = true;
7272
stopHandler.run();
73-
log.debug("Reading next element failed with error {})", e.getClass().getSimpleName());
73+
log.debug("Reading next element failed with error {}.", e.getClass().getSimpleName());
7474
throw errorHandler.apply(e);
7575
}
7676
return !isDone;

core/src/main/java/com/sap/ai/sdk/core/common/RequestLogContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public static void logRequestStart() {
7777
val reqId = UUID.randomUUID().toString().substring(0, 8);
7878
RequestLogContext.setRequestId(reqId);
7979

80-
val message = "[reqId={}] Starting {} {} request to {}, destination={}";
80+
val message = "[reqId={}] Starting {} {} request to {}, destination={}.";
8181
log.debug(
8282
message,
8383
reqId,

docs/adrs/006-logging-strategy.md

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ This approach mandates descriptive, human-readable logs with structured request
3838
Logs must be clear enough for a developer to understand what happened without checking the code.
3939
Use the `metric=value` pattern to include structured details with extensibility in mind.
4040

41-
```[reqId=e3eaa45c] OpenAI request completed successfully with duration=1628ms, size=1,2KB.```
41+
```[reqId=e3eaa45c] OpenAI request completed successfully with duration=1628ms, responseSize=1.2KB.```
4242

4343
* **Correlate logs.**
4444
Include a request identifier (e.g., `reqId`) in per-request logs to assist with correlation and debugging.
45+
In this SDK, a "request" represents a single AI service operation (e.g., one chat completion call, one embedding generation).
46+
The `reqId` is generated for each AI service call and is distinct from any HTTP request tracking in user's application framework.
4547

4648
* **Exception logging.**
4749
When logging exceptions, use standard logging methods (e.g., `log.error("Operation failed", exception)`) rather than serializing exception objects.
@@ -59,16 +61,42 @@ This approach mandates descriptive, human-readable logs with structured request
5961

6062
* **Avoid unnecessary warnings.**
6163
Use the WARN level only for actionable or genuinely concerning conditions.
62-
Do not use it as a placeholder or for expected transient states.
64+
Expected transient states (retries, fallbacks, cache misses) should not generate a warning.
6365

6466
* **Explicit request logging.**
6567
Always log at **request start** to provide immediate visibility that an operation has begun.
6668
This helps users understand that their request is being processed even before a result is available.
6769
Do not rely solely on response-time logging — requests may fail, hang, or take long durations.
6870
This approach also avoids the need for stack-trace investigation when surface error responses are ambiguous.
6971

72+
```[reqId=e3eaa45c] Starting OpenAI synchronous request to /v1/chat/completions, destination=<some-uri>.```
73+
7074
* **Performance-aware logging.**
71-
If a log statement requires expensive computation, guard it with a log-level check (e.g., `if (log.isDebugEnabled())`) to prevent performance degradation.
75+
If a log statement requires any object creation, guard it with a log-level check to prevent performance degradation.
76+
77+
``` java
78+
Optional<Destination> maybeDestination;
79+
Destination fallbackDestination = /* existing instance */;
80+
81+
// Bad: Creates objects or invokes methods even when logging is disabled
82+
log.debug("Destination: {}", maybeDestination.toString());
83+
log.debug("Destination: {}", maybeDestination.orElseGet(() -> new Destination()));
84+
log.debug("Destination: {}", maybeDestination.orElseGet(this::getFallback));
85+
log.debug("Destination: {}", maybeDestination.orElse(getFallback()));
86+
87+
// Good: No object creation or method invocation
88+
log.debug("Destination: {}", maybeDestination);
89+
log.debug("Destination: {}", maybeDestination.orElse(fallbackDestination));
90+
91+
// Good: Guard object creation
92+
if (log.isDebugEnabled()) {
93+
log.debug("Destination: {}", maybeDestination.orElse(getFallback()));
94+
}
95+
96+
// Exception: Singletons require no guarding (no object creation)
97+
Optional<List<Destination>> maybeDestinations;
98+
log.debug("Destinations: {}", maybeDestinations.orElse(Collections.emptyList()));
99+
```
72100

73101
---
74102

@@ -82,13 +110,35 @@ This approach mandates descriptive, human-readable logs with structured request
82110
Per-request MDC context must be cleared when the response completes.
83111
Avoid setting per-request values in long-lived objects that outlive the request lifecycle, as this can result in corrupted or incomplete log context.
84112

85-
* **Granular clearing only.**
86-
Never clear the entire MDC context.
87-
Instead, remove entries key-by-key to preserve unrelated context items that may remain valid for longer periods.
88-
89113
* **Centralized MDC management.**
90114
Avoid using magic strings for MDC keys or values.
91115
Define them in a dedicated structure or utility (e.g., `RequestLogContext` class) to ensure discoverability and prevent errors during refactoring.
116+
```java
117+
// Bad: Magic strings scattered in code
118+
MDC.put("service", "OpenAI");
119+
log.debug("Service {}", MDC.get("service"));
120+
121+
// Good: Centralized in utility class
122+
RequestLogContext.setService(Service.OPENAI);
123+
log.debug("Service {}", RequestLogContext.get(MdcKeys.SERVICE));
124+
```
125+
126+
* **Granular clearing only.**
127+
Never clear the entire MDC context.
128+
Instead, remove entries key-by-key to preserve unrelated context items that may remain valid for longer periods.
129+
```java
130+
// Bad: Risk clearing useful context entries from other components
131+
MDC.clear();
132+
133+
// Good: Clear only your own entries, centralized in utility class
134+
class RequestLogContext {
135+
//...
136+
static void clear(){
137+
MDC.remove(MdcKeys.REQUEST_ID);
138+
MDC.remove(MdcKeys.SERVICE);
139+
}
140+
}
141+
```
92142

93143
* **Responsibility and ownership.**
94144
The component or class that sets MDC context values is also responsible for clearing them.

foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public List<OpenAiToolMessage> executeTools() {
116116
final var tools = Optional.ofNullable(originalRequest.getToolsExecutable()).orElseGet(List::of);
117117
if (log.isDebugEnabled() && !tools.isEmpty()) {
118118
final var toolNames = tools.stream().map(OpenAiTool::getName).toList();
119-
log.debug("Executing {} tool call(s) - {}", toolNames.size(), toolNames);
119+
log.debug("Executing {} tool call(s) - {}.", toolNames.size(), toolNames);
120120
}
121121
return OpenAiTool.execute(tools, getMessage());
122122
}

foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTool.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private static Map<OpenAiFunctionCall, Object> executeInternal(
193193
if (toolCall instanceof OpenAiFunctionCall functionCall) {
194194
final var tool = toolMap.get(functionCall.getName());
195195
if (tool == null) {
196-
log.warn("Tool not found for function call: {}", functionCall.getName());
196+
log.warn("Tool not found for function call: {}.", functionCall.getName());
197197
continue;
198198
}
199199
final var toolResult = executeFunction(tool, functionCall);

foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/spring/OpenAiChatModel.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public ChatResponse call(@Nonnull final Prompt prompt) {
7171
if (options != null && isInternalToolExecutionEnabled(options) && response.hasToolCalls()) {
7272
val toolCalls =
7373
response.getResult().getOutput().getToolCalls().stream().map(ToolCall::name).toList();
74-
log.info("Executing {} tool call(s) - {}", toolCalls.size(), toolCalls);
74+
log.info("Executing {} tool call(s) - {}.", toolCalls.size(), toolCalls);
7575
val toolExecutionResult = toolCallingManager.executeToolCalls(prompt, response);
7676
// Send the tool execution result back to the model.
7777
log.debug("Re-invoking model with tool execution results.");
@@ -219,7 +219,7 @@ private static List<ChatCompletionTool> extractTools(final ToolCallingChatOption
219219
val tool = new ChatCompletionTool().type(toolType).function(toolFunction);
220220
tools.add(tool);
221221
} catch (JsonProcessingException e) {
222-
log.warn("Failed to add tool to the chat request: {}", e.getMessage());
222+
log.warn("Failed to add tool to the chat request: {}.", e.getMessage());
223223
}
224224
}
225225
return tools;

0 commit comments

Comments
 (0)