Flattened bundle. Content from source markdown guides is inlined below.
This guide provides step-by-step instructions for integrating AWS AppConfig into your application for dynamic configuration management, particularly for dynamically changing log levels in avaje-simple-logger.
AWS AppConfig is an AWS service that enables you to:
- Store and manage application configuration separately from code
- Deploy configuration changes without redeploying applications
- Validate configuration syntax before deployment
- Monitor configuration changes and rollback if needed
- Use the same configuration across multiple environments
When integrated with avaje-simple-logger, you can change log levels dynamically without restarting your application.
Before starting, verify the following:
- AWS account with AppConfig access
- Application is already using avaje-simple-logger (or following Add avaje-simple-logger to Maven Project guide)
- Application uses avaje-config (for configuration management)
- AWS AppConfig agent is available in your deployment environment (EC2, ECS, Lambda, on-premises)
- Java 11 or later
- IAM permissions to read from AWS AppConfig
Your application needs IAM permissions for:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"appconfig:GetConfiguration",
"appconfig:StartConfigurationSession"
],
"Resource": "arn:aws:appconfig:*:*:application/*"
}
]
}When AppConfig is enabled:
- Application starts and initializes avaje-config
- avaje-aws-appconfig plugin loads initial configuration from AppConfig
- Configuration is merged with local properties files
- Plugin polls AppConfig at regular intervals (default 45 seconds)
- When changes are detected, configuration is updated in-memory
- Log levels (and other settings) take effect immediately
- No application restart required
Add avaje-aws-appconfig to your pom.xml:
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-aws-appconfig</artifactId>
<version>2.8</version>
</dependency>Important: This dependency includes avaje-config, which is required for dynamic configuration.
If you want both avaje-simple-logger AND dynamic configuration support:
<!-- Dynamic logging with AppConfig -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-simple-logger</artifactId>
<version>1.5-RC1</version>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-aws-appconfig</artifactId>
<version>2.8</version>
</dependency>- Go to AWS AppConfig Console
- Click Create Application
- Enter Application name (e.g.,
my-application) - Click Create Application
- In your application, click Create Environment
- Enter Environment name (e.g.,
dev,staging,prod) - Click Create Environment
- In your environment, click Create Configuration Profile
- Enter Configuration Profile name (e.g.,
default) - Select Content type: Choose
application/x-propertiesfor properties format orapplication/x-yamlfor YAML format - Click Create Configuration Profile
Now create the actual configuration content.
Click Create Configuration and enter:
logger.defaultLogLevel=warn
logger.format=json
log.level.com.mycompany=debug
log.level.io.avaje=infoClick Create Configuration and enter:
logger:
defaultLogLevel: warn
format: json
log:
level:
com.mycompany: debug
io.avaje: info- Click Save configuration and review the content
- Click Start Deployment
- Select or create a Deployment Strategy (Immediate for quick testing, All at once or Linear for gradual rollout)
- Click Deploy
Create src/main/application.yaml (or update if exists):
aws:
appconfig:
enabled: true
application: my-application # Must match AWS AppConfig application name
environment: ${ENVIRONMENT:dev} # Environment variable, defaults to 'dev'
configuration: default # Configuration profile name
pollingEnabled: true
pollingSeconds: 45 # Check for changes every 45 secondsCreate src/main/resources/avaje-logger.properties:
logger.defaultLogLevel=warn
logger.format=json
logger.component=my-service
logger.environment=production
log.level.com.mycompany=info
log.level.io.avaje=warnThese are the local defaults. AWS AppConfig will override these if different values are configured there.
Create src/test/application-test.yaml:
aws:
appconfig:
enabled: falseThis prevents tests from making network calls to AWS AppConfig.
For local development without AWS credentials, create src/main/resources/application-local.yaml:
aws:
appconfig:
enabled: falseThen run your application with: java -Dspring.profiles.active=local -jar myapp.jar
| Property | Type | Default | Description |
|---|---|---|---|
aws.appconfig.enabled |
boolean | true |
Enable/disable AppConfig plugin |
aws.appconfig.application |
string | (required) | AWS AppConfig application name |
aws.appconfig.environment |
string | (required) | AWS AppConfig environment name |
aws.appconfig.configuration |
string | default |
Configuration profile name |
aws.appconfig.pollingEnabled |
boolean | true |
Enable polling for changes |
aws.appconfig.pollingSeconds |
integer | 45 |
Poll interval in seconds |
aws.appconfig.refreshSeconds |
integer | pollingSeconds - 1 |
Max time to wait for changes |
In your AppConfig configuration, you can set:
# Set default log level for entire application
logger.defaultLogLevel=warn
# Set specific package log levels
log.level.com.mycompany=debug
log.level.com.mycompany.database=trace
log.level.io.avaje=info
# Set log format (json or plain)
logger.format=jsonIf the AppConfig agent is not available or configuration cannot be loaded:
- Application uses local
avaje-logger.propertiesvalues - Logs a warning about AppConfig unavailability
- Continues to function normally with local configuration
- Will periodically retry connecting to AppConfig
If you want to develop without AWS access:
- Disable AppConfig in
application-local.yaml - Use local
avaje-logger.propertiesfor configuration - Any changes require application restart (normal behavior)
Build your application:
mvn clean packageWhen running, ensure environment variables are set:
export ENVIRONMENT=prod
export AWS_REGION=us-east-1
java -jar myapp.jarAWS Lambda has the AppConfig agent built-in. Simply:
- Set environment variables in Lambda configuration:
ENVIRONMENT=prodAWS_REGION=us-east-1
- Grant IAM permissions (see Prerequisites section)
- Deploy your application
- Ensure ECS task role has AppConfig permissions
- Set environment variables in task definition:
{ "name": "ENVIRONMENT", "value": "prod" } - Deploy task
- Install AWS AppConfig agent on EC2 instance
- Start the agent:
sudo systemctl start aws-appconfig-agent - Set environment variables:
export ENVIRONMENT=prod export AWS_REGION=us-east-1
- Run your application
To change log levels without restarting:
- Go to AWS AppConfig Console
- Navigate to your Application → Environment → Configuration Profile
- Click Update configuration
- Change the log levels (e.g.,
log.level.com.mycompany=trace) - Click Save and Deploy
- Within the polling interval (default 45 seconds), your application will update
- Go to AWS AppConfig Console
- Click View deployments
- Select the deployment to rollback
- Click Rollback
- Confirm the rollback
Add logging to see when configuration changes are applied:
import io.avaje.config.Config;
import io.avaje.config.Configuration;
// Listen for configuration changes
Configuration.onChange((Map<String, String> changes) -> {
for (var entry : changes.entrySet()) {
if (entry.getKey().startsWith("log.level.") ||
entry.getKey().startsWith("logger.")) {
System.err.println("Log config changed: " + entry.getKey() +
" = " + entry.getValue());
}
}
});Cause: AWS credentials not available or AppConfig agent not running.
Solution:
- Check AWS credentials are configured (IAM role, environment variables, or credentials file)
- Check AppConfig agent is running:
ps aux | grep aws-appconfig-agent - Check network connectivity to AppConfig endpoint
- Verify security group allows outbound traffic to AppConfig port (port 2371 by default)
Cause: Application hasn't checked for changes yet.
Solution:
- Wait for the polling interval (default 45 seconds)
- Force refresh by calling
Configuration.refresh()in your application - Check that
aws.appconfig.pollingEnabledistrue - Verify AppConfig deployment status is "DEPLOYMENT_SUCCESS"
Cause: Application name, environment, or configuration profile name doesn't match AWS AppConfig.
Solution:
- Verify exact names match in application.yaml:
aws.appconfig: application: my-application # Match exact AWS AppConfig name environment: prod # Match exact environment name configuration: default # Match exact profile name
- Check AWS AppConfig console for exact names
Cause: AppConfig not enabled or configuration wasn't deployed.
Solution:
- Check
aws.appconfig.enabledistruein application.yaml - Check deployment status in AWS AppConfig console (should be "DEPLOYMENT_SUCCESS")
- Check log levels are correctly formatted in AppConfig configuration
- Wait for polling interval and check application logs
Cause: AppConfig enabled in test environment.
Solution:
- Create
src/test/application-test.yamlwithaws.appconfig.enabled: false - Ensure test profile is active when running tests
- For Maven:
mvn testautomatically uses test profile - For IDE: Configure test runner to use test profile
Cause: Trying to run application locally without AWS credentials.
Solution:
- Create
src/main/resources/application-local.yaml:aws.appconfig: enabled: false
- Run with:
java -Dspring.profiles.active=local -jar myapp.jar(if using Spring) - Or set in code:
System.setProperty("spring.profiles.active", "local") - Use local
avaje-logger.propertiesfor configuration
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>my-service</artifactId>
<version>1.0.0</version>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-simple-logger</artifactId>
<version>1.5-RC1</version>
</dependency>
<!-- Dynamic Configuration via AppConfig -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-aws-appconfig</artifactId>
<version>2.8</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>spring:
application:
name: my-service
aws:
appconfig:
enabled: true
application: my-service-app
environment: ${ENVIRONMENT:dev}
configuration: default
pollingEnabled: true
pollingSeconds: 45logger.defaultLogLevel=warn
logger.format=json
logger.component=my-service
logger.environment=${ENVIRONMENT:dev}
log.level.com.mycompany=info
log.level.com.mycompany.database=debug
log.level.io.avaje=warn
log.level.org.springframework=infoaws:
appconfig:
enabled: falselogger.defaultLogLevel=info
logger.format=plain
log.level.com.mycompany=debug
log.level.io.avaje=debugimport org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.avaje.config.Configuration;
@SpringBootApplication
public class MyServiceApplication {
private static final Logger log = LoggerFactory.getLogger(MyServiceApplication.class);
public static void main(String[] args) {
// Listen for configuration changes
Configuration.onChange(changes -> {
for (var entry : changes.entrySet()) {
if (entry.getKey().startsWith("log.level.") ||
entry.getKey().startsWith("logger.")) {
System.err.println("Logging config changed: " + entry.getKey() +
" = " + entry.getValue());
}
}
});
SpringApplication.run(MyServiceApplication.class, args);
log.info("My Service started successfully");
}
}<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>my-lambda</artifactId>
<version>1.0.0</version>
<dependencies>
<!-- AWS Lambda -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.2</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-simple-logger</artifactId>
<version>1.5-RC1</version>
</dependency>
<!-- Dynamic Configuration via AppConfig -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-aws-appconfig</artifactId>
<version>2.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>aws:
appconfig:
enabled: true
application: my-lambda-app
environment: ${ENVIRONMENT:dev}
configuration: default
pollingEnabled: true
pollingSeconds: 60logger.defaultLogLevel=warn
logger.format=json
logger.component=my-lambda
log.level.com.mycompany=info
log.level.com.mycompany.handlers=debugimport com.amazonaws.lambda.core.Context;
import com.amazonaws.lambda.core.RequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.avaje.config.Configuration;
public class MyLambdaHandler implements RequestHandler<String, String> {
private static final Logger log = LoggerFactory.getLogger(MyLambdaHandler.class);
static {
// Listen for configuration changes
Configuration.onChange(changes -> {
for (var entry : changes.entrySet()) {
if (entry.getKey().startsWith("log.level.") ||
entry.getKey().startsWith("logger.")) {
System.err.println("Logging config changed: " + entry.getKey() +
" = " + entry.getValue());
}
}
});
}
@Override
public String handleRequest(String input, Context context) {
log.info("Lambda invoked with input: {}", input);
return "Success";
}
}- Create Lambda function with Java runtime
- Upload JAR file (produced by
mvn clean package) - Set handler:
com.mycompany.MyLambdaHandler::handleRequest - Add environment variables:
ENVIRONMENT=prodAWS_REGION=us-east-1
- Attach IAM role with AppConfig permissions
- Set timeout to at least 30 seconds (for initial AppConfig load)
Access configuration values in your code:
import io.avaje.config.Config;
// Get string value
String level = Config.get("log.level.com.mycompany", "info");
// Get integer value
int pollingSeconds = Config.getInt("aws.appconfig.pollingSeconds", 45);
// Get boolean value
boolean enabled = Config.getBool("aws.appconfig.enabled", true);
// Listen for changes
Config.onChange(changes -> {
if (changes.containsKey("log.level.com.mycompany")) {
String newLevel = changes.get("log.level.com.mycompany");
System.out.println("Log level changed to: " + newLevel);
}
});You now have AWS AppConfig integrated into your application for dynamic configuration!
- ✅ Created AWS AppConfig application and environment
- ✅ Created configuration profile with logging settings
- ✅ Added avaje-aws-appconfig dependency
- ✅ Configured application.yaml with AppConfig settings
- ✅ Set up local properties as fallback
- ✅ Disabled AppConfig for tests
- ✅ Deployed application with AppConfig support
- 🚀 Change log levels without restarting your application
- 🔄 Gradual configuration rollout with deployment strategies
- ↩️ Rollback configuration if problems occur
- 🛡️ AWS AppConfig validates syntax before deployment
- 📊 Monitor configuration changes in real-time
- 🔒 Centralized configuration management across multiple services
- Customize polling interval - Adjust
pollingSecondsbased on your needs - Test configuration changes - Update AppConfig configuration and verify application responds
- Set up monitoring - Log configuration changes to track when they occur
- Add more settings - Use AppConfig for other application settings beyond logging
- Implement gradual rollout - Use AppConfig deployment strategies for safer changes