Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/log-deployments-and-changes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Log deployments and changes example

on:
workflow_dispatch:

permissions:
contents: read

jobs:
demonstrate:
runs-on: ubuntu-22.04
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'

- name: Build example
run: ./mvnw -B -pl microservices-log-deployments-and-changes -am package

- name: Run simulated deployment and change pipeline
shell: bash
run: |
set -o pipefail
java -jar microservices-log-deployments-and-changes/target/microservices-log-deployments-and-changes-1.26.0-SNAPSHOT.jar 2>&1 | tee deployment-events.log

- name: Preserve timeline and alerts
if: always()
uses: actions/upload-artifact@v4
with:
name: deployment-events
path: deployment-events.log
if-no-files-found: warn
172 changes: 172 additions & 0 deletions microservices-log-deployments-and-changes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
title: "Microservices Log Deployments and Changes Pattern in Java"
shortTitle: Microservices Log Deployments and Changes
description: "Record operational changes in a shared timeline to correlate service failures with deployments and configuration updates."
category: Integration
language: en
tag:
- Microservices
- Enterprise patterns
- Fault tolerance
---

## Intent of Microservices Log Deployments and Changes Design Pattern

Record deployments and operational changes with their service, version, environment, initiator,
timestamp, and outcome. Use this history to correlate changes with unexpected application behavior.

## Detailed Explanation of Microservices Log Deployments and Changes Pattern with Real-World Examples

Real-world example

> An online shop releases a new orders service and changes the payments timeout. Payments then
> start failing. A shared timeline helps operators identify which changes preceded the failures,
> even though different teams performed the operations.

In plain words

> Keep a shared record of what changed, when, who changed it, and whether it succeeded.

[Microservices.io](https://microservices.io/patterns/observability/log-deployments-and-changes.html)
describes recording deployments and environment changes to correlate them with application issues.

This example follows the producer-to-central-store structure of
[Microservices Log Aggregation](../microservices-log-aggregation/README.md). Its events describe
operational changes rather than general application messages. Each `DeploymentPipeline` wraps a
service operation and appends its outcome to the shared `CentralLogStore`. `LogMonitor` presents
the history and selects alerts using a supplied rule.

## Class diagram

```mermaid
classDiagram
DeploymentPipeline --> CentralLogStore : appends outcomes
CentralLogStore o-- ChangeEvent : retains history
ChangeEvent --> ChangeType
LogMonitor --> CentralLogStore : reads history
class ChangeEvent {
Instant timestamp
String service
String version
String environment
String actor
ChangeType type
String description
boolean successful
}
class ChangeType {
<<enumeration>>
DEPLOYMENT
CONFIGURATION
ENDPOINT
PROTOCOL
}
```

## Programmatic Example of Microservices Log Deployments and Changes Pattern in Java

Create a shared store and inject a clock. Tests use a fixed clock for deterministic UTC timestamps.

```java
var store = new CentralLogStore();
var pipeline = new DeploymentPipeline(store, Clock.systemUTC());
var monitor = new LogMonitor(store);
pipeline.execute(
"orders", "2.0", "production", "release-engineer", ChangeType.DEPLOYMENT,
"Deploy orders release", () -> LOGGER.info("Deploying orders 2.0"));
```

The operation represents deployment tooling or a configuration update. `execute` invokes it once
and records an immutable `ChangeEvent` in a `finally` block. A successful return records success;
an exception records failure and propagates to the caller. The timestamp is the completion time.
Configuration, endpoint, and communication protocol updates use the corresponding `ChangeType`.
Callers do not need a separate logging step.

Display the timeline and alert on failed production operations:

```java
LOGGER.info("Deployment and change timeline:\n{}", monitor.timeline());
monitor.alerts(event -> !event.successful() && "production".equals(event.environment()))
.forEach(event -> LOGGER.warn("ALERT: {} {} failed", event.service(), event.type()));
```

`App` simulates changes for orders and payments. Its protocol update deliberately fails; the demo
catches that failure to display the complete history. Output includes these lines (times and actor vary):

```text
2026-01-01T12:00:00Z | orders | 2.0 | production | release-engineer | DEPLOYMENT | SUCCESS | Deploy orders release
2026-01-01T12:00:00Z | payments | 1.1 | production | release-engineer | PROTOCOL | FAILURE | Switch inter-service protocol to gRPC
ALERT: payments PROTOCOL failed
```

### Running and verifying the example

From the repository root, with Java 21:

```shell
./mvnw -pl microservices-log-deployments-and-changes -am test
./mvnw -pl microservices-log-deployments-and-changes spotless:check
./mvnw -pl microservices-log-deployments-and-changes -am package -DskipTests
java -jar microservices-log-deployments-and-changes/target/microservices-log-deployments-and-changes-1.26.0-SNAPSHOT.jar
```

On Windows, use `./mvnw.cmd` instead of `./mvnw`. Tests cover all event types, complete metadata,
exception propagation, and immutable snapshots. `LogMonitorTest` also integrates two producers
with the shared store and verifies the rendered timeline and selective alerts.

### CI/CD integration

The repository's `.github/workflows/log-deployments-and-changes.yml` defines the manually triggered
**Log deployments and changes example** workflow. It follows the existing Java 21/Maven setup,
builds this module, and runs the demonstration. `App` reads `GITHUB_ACTOR` for the initiator. The
workflow saves the console timeline and alerts as a downloadable artifact. Once the workflow is
on the default branch, run it from the repository's Actions tab.

This is a simulated pipeline; it does not deploy the repository's other examples. For a real service,
wrap its deployment or change command with `DeploymentPipeline.execute`, supplying the actual
version, environment, and initiator. The operation must throw on failure, including when an external
command returns a nonzero exit code. Share the event destination across services. Production callers
should let failures fail the pipeline; only the demo catches its deliberately simulated failure.

The store is single-threaded and in-memory, and visualization is a console timeline. It does not
retain history between processes, detect out-of-band changes, or send external notifications.
Production use needs durable shared collection, delivery handling, and monitoring that overlays
change timestamps on service metrics. Abrupt process termination can prevent a completion event
from being written. Descriptions should not contain credentials or raw configuration secrets.

## When to Use the Microservices Log Deployments and Changes Pattern in Java

* Multiple services or teams deploy independently.
* Operators need to correlate incidents with releases and environment changes.
* Configuration, endpoint, or protocol changes affect behavior without a new release.

## Real-World Applications of Microservices Log Deployments and Changes Pattern in Java

* Deployment tools can publish release markers alongside application metrics, as described by
[Microservices.io](https://microservices.io/patterns/observability/log-deployments-and-changes.html).
* Service delivery pipelines can record releases and configuration updates in one operational history.

## Benefits and Trade-offs of Microservices Log Deployments and Changes Pattern

Benefits:

* Makes recent changes visible across service and team boundaries.
* Automates recording at the operation boundary, including failed attempts.
* Supports filtering by environment, service, type, or outcome.

Trade-offs:

* Every deployment and change path must participate for complete history.
* Durable collection and reliable delivery add operational work.
* Temporal correlation aids investigation but does not prove causation.

## Related Java Design Patterns

* [Microservices Log Aggregation](../microservices-log-aggregation/README.md): centralizes application logs.
* [Event Sourcing](../event-sourcing/README.md): reconstructs state from events; this example records operational outcomes.
* [Observer](../observer/README.md): can notify subscribers when change events arrive.

## References and Credits

* [Pattern: Log deployments and changes](https://microservices.io/patterns/observability/log-deployments-and-changes.html)
* [Issue #2696](https://github.com/iluwatar/java-design-patterns/issues/2696)
74 changes: 74 additions & 0 deletions microservices-log-deployments-and-changes/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).

The MIT License
Copyright © 2014-2022 Ilkka Seppälä

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>

<artifactId>microservices-log-deployments-and-changes</artifactId>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
Comment thread
arnabnandy7 marked this conversation as resolved.
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.logdeploymentsandchanges.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.logdeploymentsandchanges;

import java.time.Clock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Demonstrates automatic deployment and change logging for two services. */
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/** Run simulated pipeline steps, then display their timeline and failure alerts. */
public static void main(String[] args) {
var store = new CentralLogStore();
var pipeline = new DeploymentPipeline(store, Clock.systemUTC());
var monitor = new LogMonitor(store);
var actor = System.getenv().getOrDefault("GITHUB_ACTOR", "release-engineer");

pipeline.execute(
"orders",
"2.0",
"production",
actor,
ChangeType.DEPLOYMENT,
"Deploy orders release",
() -> LOGGER.info("Deploying orders 2.0"));
pipeline.execute(
"payments",
"1.1",
"production",
actor,
ChangeType.CONFIGURATION,
"Increase request timeout",
() -> LOGGER.info("Updating payments timeout"));
pipeline.execute(
"orders",
"2.0",
"production",
actor,
ChangeType.ENDPOINT,
"Route payments to /v2/payments",
() -> LOGGER.info("Updating payments endpoint"));
try {
pipeline.execute(
"payments",
"1.1",
"production",
actor,
ChangeType.PROTOCOL,
"Switch inter-service protocol to gRPC",
() -> {
throw new IllegalStateException("Protocol health check failed");
});
} catch (IllegalStateException exception) {
LOGGER.warn("Simulated change failed: {}", exception.getMessage());
}

LOGGER.info("Deployment and change timeline:\n{}", monitor.timeline());
monitor
.alerts(event -> !event.successful() && "production".equals(event.environment()))
.forEach(event -> LOGGER.warn("ALERT: {} {} failed", event.service(), event.type()));
}
}
Loading
Loading