I've had an Elastic Beanstalk instance streaming my logs to Cloud Watch for about a year. This week the logs stopped streaming. This may have been because I 'rebuilt' the environment in Beanstalk. No configuration changes were made at the same time.
I've double checked that my Beanstalk role has the correct permissions in IAM (it has CloudWatchFullAccess).
I also tried deleting all of my existing group logs. I then went into the Beanstalk 'Instance log streaming to CloudWatch Logs' area, changed my log retention period and restarted the App Server. Sure enough my log groups were recreated (with the new retention period), so I'm pretty sure the permissions look OK. Despite this, no log messages are appearing in the log groups.
I have requested the recent logs though Beanstalk and I can see messages are being written to the logs on the App Server OK.
My platform is Tomcat 8 with Java 8 running on 64bit Amazon Linux/2.6.2
I'm not sure where to go from here. I have no error messages to work off, or any good ideas for what to check next.
Edit: Here is my custom config for CloudWatch, as defined here
files:
"/etc/awslogs/config/company_log.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/log/tomcat8/company.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat8/company.log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat8/company.*
It's probably because it's not allowed to. Make sure the role used by the EC2 instances is allowed to access:
logs:CreateLogGroup
logs:CreateLogStream
logs:GetLogEvents
logs:PutLogEvents
logs:DescribeLogGroups
logs:DescribeLogStreams
logs:PutRetentionPolicy
With Elastic Beanstalk the IAM role used by the EC2 instances is probably aws-elasticbeanstalk-ec2-role. Give that role access to a new policy: ec2-cloudwatch-logs-stream (if someone knows a better name let me know) using JSON (as suggested here):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:GetLogEvents",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutRetentionPolicy"
],
"Resource": [
"*"
]
}
]
}
And it should work after restarting the awslogs service with sudo services awslogs restart. If not; check the logs. You can find it here: /var/log/awslogs.log
You might have upgraded your AWS AMI platform, the Tomcat location is therefore different (e.g. /var/log/tomcat instead of /var/log/tomcat8).
I've detailed in a new Medium blog how this all works and an example .ebextensions file and where to put it.
Below is an excerpt that you might be able to use, though the article explains how to determine the right folder/file(s) to stream.
packages:
yum:
awslogs: []
option_settings:
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: StreamLogs
value: true
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: DeleteOnTerminate
value: false
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: RetentionInDays
value: 90
files:
"/etc/awslogs/awscli.conf" :
mode: "000600"
owner: root
group: root
content: |
[plugins]
cwlogs = cwlogs
[default]
region = `{"Ref":"AWS::Region"}`
"/etc/awslogs/config/logs.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/log/tomcat/localhost.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/localhost.log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/localhost.*
[/var/log/tomcat/catalina.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/catalina.log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/catalina.*
[/var/log/tomcat/localhost_access_log.txt]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/access_log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/access_log.*
commands:
"01":
command: systemctl enable awslogsd.service
"02":
command: systemctl restart awslogsd
Related
I've been having issues setting up custom log file streaming to CloudWatch from my AWS Elastic Beanstalk project. For reference, I've tried the suggestions in "AWS Elastic Beanstalk: Add custom logs to CloudWatch?" to no avail. Essentially, when I download the logs off the host, the log files I want are located at /var/log/containers/api-1c080332ba3f-stdouterr.log, /var/log/containers/nginx-a5057f87f4cf-stdouterr.log, and /var/log/containers/web-0a2e0762e8f0-stdouterr.log (where the numbers change on each update).
I added a new custom log config file in .ebextensions/log.config (following this for reference):
packages:
yum:
awslogs: []
files:
"/etc/awslogs/awscli.conf" :
mode: "000600"
owner: root
group: root
content: |
[plugins]
cwlogs = cwlogs
[default]
region = `{"Ref":"AWS::Region"}`
"/etc/awslogs/awslogs.conf" :
mode: "000600"
owner: root
group: root
content: |
[general]
state_file = /var/lib/awslogs/agent-state
"/etc/awslogs/config/logs.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/log/containers/nginx-stdouterr.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/containers/nginx-stdouterr.log"]]}`
log_stream_name = {instance_id}
file = /var/log/containers/nginx*
[/var/log/containers/web-stdouterr.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/containers/web-stdouterr.log"]]}`
log_stream_name = {instance_id}
file = /var/log/containers/web*
[/var/log/containers/api-stdouterr.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/containers/api-stdouterr.log"]]}`
log_stream_name = {instance_id}
file = /var/log/containers/api*
commands:
"01":
command: systemctl enable awslogsd.service
"02":
command: systemctl restart awslogsd
I've additionally added this policy to the service and ec2 roles:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Resource": [
"*"
]
}
]
}
Finally, I don't see any errors in /var/logs/awslogs.log.
Is there any other piece I am missing? Looked through the official documentation with no luck so far.
Instead of adding your own policy to the instance role, try adding the CloudWatchAgentServerPolicy managed policy.
I also removed the /etc/awslogs/awslogs.conf file definition when mine worked.
Finally, the agent only seems to create log groups in CloudWatch when lines are written to the log files. Make sure the files you are collecting are getting written to and see if the log groups get created. Good luck!
I want to stream tomcat catalinat.out logs to cloud watch:
This is my config I follow:
https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/instance-configuration/logs-streamtocloudwatch-linux.config
But I don't see catalina.out in cloudwatch console :
This is error , I have in awslogs.log
How can I solve it.
2020-05-22 18:15:55,450 - cwlogs.push.batch - WARNING - 3374 - Thread-29 - CreateLogGroup failed with exception An error occurred (AccessDeniedException) when calling the CreateLogGroup operation: User: arn:aws:sts::610232524349:assumed-role/aws-elasticbeanstalk-ec2-role/i-099300c0bfd4b6a28 is not authorized to perform: logs:CreateLogGroup on resource: arn:aws:logs:eu-central-1:610232524349:log-group:/aws/elasticbeanstalk/************/var/log/tomcat8/catalina.out:log-stream:
With the sample provided you are not exporting the catalinat.out you are streaming to cloudwatch the following files:
/var/log/dmesg
/var/log/messages
To stream the catalitat.out you have to add the file to the configuration with the location of the log at the end of content section (Lines 61-71 on the sample provided)
It should be something like this replacing /path/to/catalitat.log with the actual path to the log:
[/path/to/catalitat.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "/path/to/catalitat.log"]]}`
log_stream_name = {instance_id}
file = /path/to/catalitat.log
Steps to publish tomcat logs (catalina.out) to the CloudWatch stream
Create a new policy for EC2 to use AWS CloudWatch, providing access to create log groups, log streams and publish logs
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "logs:*",
"Resource": "*"
}
]
}
Attach the policy to newly created or existing IAM role of the EC2 instance
Connect to the instance using SSH, using the PEM/PPK file.
Locate the AWS CloudWatch Logs Agent configuration file
[ec2-user# elasticbeanstalk]$ sudo su
[root# elasticbeanstalk]# find / -name "*awslogs.conf"
/etc/awslogs/awslogs.conf
Edit the configuration file and add the entry for a log stream for tomcat logs. I have used catalina.out
[ec2-user# elasticbeanstalk]$ cat /etc/awslogs/awslogs.conf
[general]
state_file = /var/lib/awslogs/agent-state
[tomcatLogs]
log_group_name = tomcatLogs
log_stream_name = catalinaLogs
time_zone = LOCAL
file = /[your-path-to]/tomcat8/catalina.out
[ec2-user# elasticbeanstalk]$
Restart the service AWS Logs
[ec2-user# elasticbeanstalk]$ sudo service awslogs restart
Revisit the CloudWatch log groups page, where you can see the new group is created with the name “tomcatLogs” and a log stream with the name “catalinaLogs”
I feel your pain! I've detailed in a new Medium blog how this all works and an example .ebextensions file and where to put it.
Below is an excerpt that you might be able to use, though the article explains how to determine the right folder/file(s) to stream.
packages:
yum:
awslogs: []
option_settings:
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: StreamLogs
value: true
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: DeleteOnTerminate
value: false
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: RetentionInDays
value: 90
files:
"/etc/awslogs/awscli.conf" :
mode: "000600"
owner: root
group: root
content: |
[plugins]
cwlogs = cwlogs
[default]
region = `{"Ref":"AWS::Region"}`
"/etc/awslogs/config/logs.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/log/tomcat/localhost.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/localhost.log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/localhost.*
[/var/log/tomcat/catalina.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/catalina.log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/catalina.*
[/var/log/tomcat/localhost_access_log.txt]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat/access_log"]]}`
log_stream_name = {instance_id}
file = /var/log/tomcat/access_log.*
commands:
"01":
command: systemctl enable awslogsd.service
"02":
command: systemctl restart awslogsd
I have been struggling to get basic metrics from the CloudWatch agent. I've been getting this error and I have no idea what it means nor can I find resources online which talk much about it
refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
I followed the instructions here and have read through the documentation carefully. Again, the goal is just to read in some basic metrics from my EC2 instance to CloudWatch. Here are the steps I have followed:
Followed instructions here "To create the IAM role necessary to run the CloudWatch agent on EC2 instances" and then assigned it to my instance.
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
ami id is ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20190628 (ami-0cfee17793b08a293)
Install the .deb with command sudo dpkg --install --skip-same-version ./amazon-cloudwatch-agent.deb
note --install and --skip-same-version is just -i -E as done in the docs
generated a config.json with the wizard, located here /opt/aws/amazon-cloudwatch-agent/bin/config.json. I pasted the contents under the error message below.
modify the /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml file to point to new credentials of cwagent (since not using root user) with the following:
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/etc# tail -n 4 common-config.toml
#### BEGIN ANSIBLE MANAGED BLOCK ####
[credentials]
shared_credential_file = "/home/cwagent/.aws/credentials"
#### END ANSIBLE MANAGED BLOCK ####
fetch config and start agent with sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
here's the error I'm seeing in the logs now, and I'm assuming why this is why I can't see any metrics
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/logs# tail -n 20 amazon-cloudwatch-agent.log
2019/10/29 22:41:08 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json ...
2019/10/29 22:41:08 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_config.json ...
2019/10/29 22:41:08 I! Detected runAsUser: cwagent
2019/10/29 22:41:08 I! Change ownership to cwagent:cwagent
2019/10/29 22:41:08 I! Set HOME: /home/cwagent
2019-10-29T22:41:08Z I! will use file based credentials provider
2019-10-29T22:41:08Z I! cloudwatch: get unique roll up list []
2019-10-29T22:41:08Z I! Starting AmazonCloudWatchAgent (version 1.230621.0)
2019-10-29T22:41:08Z I! Loaded outputs: cloudwatch
2019-10-29T22:41:08Z I! cloudwatch: publish with ForceFlushInterval: 1m0s, Publish Jitter: 37s
2019-10-29T22:41:08Z I! Loaded inputs: disk mem
2019-10-29T22:41:08Z I! Tags enabled: host=ip-172-31-71-5
2019-10-29T22:41:08Z I! Agent Config: Interval:10s, Quiet:false, Hostname:"ip-172-31-71-5", Flush Interval:1s
2019-10-29T22:41:08Z I! will use file based credentials provider
2019-10-29T22:41:08Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
2019-10-29T22:42:37Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
2019-10-29T22:43:37Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
2019-10-29T22:46:37Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
2019-10-29T22:49:37Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
2019-10-29T22:52:37Z E! refresh EC2 Instance Tags failed: SharedCredsLoad: failed to get profile, metrics will be dropped until it got fixed
and the config.json I used
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/bin# cat config.json
{
"agent": {
"metrics_collection_interval": 10,
"run_as_user": "cwagent"
},
"metrics": {
"namespace": "TestNamespace",
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}"
},
"metrics_collected": {
"disk": {
"measurement": [
"used_percent"
],
"metrics_collection_interval": 60,
"resources": [
"*"
]
},
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 60
}
}
}
}
EDITS
I got it working after I removed the credentials modification
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/etc# tail -n 4 common-config.toml
#### BEGIN ANSIBLE MANAGED BLOCK ####
#[credentials]
#shared_credential_file = "/home/cwagent/.aws/credentials"
#### END ANSIBLE MANAGED BLOCK ####
and after I went ahead and copied the config file to the default location it checks (even though the docs say you can pass the file name as I did).
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/bin# cp config.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/bin# cd ../etc/
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/etc# chown cwagent:cwagent amazon-cloudwatch-agent.json
root#ip-172-31-71-5:/opt/aws/amazon-cloudwatch-agent/etc# ls -l
total 16
drwxr-xr-x 2 cwagent cwagent 4096 Oct 30 22:05 amazon-cloudwatch-agent.d
-rwxr-xr-x 1 cwagent cwagent 611 Oct 30 22:11 amazon-cloudwatch-agent.json
-rw-rw-r-- 1 cwagent cwagent 1144 Oct 30 22:05 amazon-cloudwatch-agent.toml
-rw-r--r-- 1 cwagent cwagent 1073 Oct 30 22:05 common-config.toml
The error appears to be related to accessing tags that are associated with Amazon EC2 instances.
The installation instructions you linked suggest creating an IAM Role with the CloudWatchAgentServerPolicy policy attached. This policy includes permission to describe tags:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"ec2:DescribeVolumes",
"ec2:DescribeTags",
"logs:PutLogEvents",
"logs:DescribeLogStreams",
"logs:DescribeLogGroups",
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*"
}
]
}
It would appear that the CloudWatch Agent on that server is not receiving such permissions, and is therefore unable to list the tags.
Therefore:
Confirm that an IAM Role has been created and that it includes the CloudWatchAgentServerPolicy policy
Confirm that this IAM Role has been assigned to the Amazon EC2 instance that is running the CloudWatch Agent
If it is still failing, check whether there are any credentials stored locally on the instance that the Agent could be using instead of the IAM Role assigned to the instance
I tested kubernetes deployment with EBS volume mounting on AWS cluster provisioned by kops. This is deployment yml file:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: helloworld-deployment-volume
spec:
replicas: 1
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: k8s-demo
image: wardviaene/k8s-demo
ports:
- name: nodejs-port
containerPort: 3000
volumeMounts:
- mountPath: /myvol
name: myvolume
volumes:
- name: myvolume
awsElasticBlockStore:
volumeID: <volume_id>
After kubectl create -f <path_to_this_yml>, I got the following message in pod description:
Attach failed for volume "myvolume" : Error attaching EBS volume "XXX" to instance "YYY": "UnauthorizedOperation: You are not authorized to perform this operation. status code: 403
Looks like this is just a permission issue. Ok, I checked policy for node role IAM -> Roles -> nodes.<my_domain> and found that there where no actions which allow to manipulate volumes, there was only ec2:DescribeInstances action by default. So I added AttachVolume and DetachVolume actions:
{
"Sid": "kopsK8sEC2NodePerms",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:AttachVolume",
"ec2:DetachVolume"
],
"Resource": [
"*"
]
},
And this didn't help. I'm still getting that error:
Attach failed for volume "myvolume" : Error attaching EBS volume "XXX" to instance "YYY": "UnauthorizedOperation: You are not authorized to perform this operation.
Am I missing something?
I found a solution. It's described here.
In kops 1.8.0-beta.1, master node requires you to tag the AWS volume with:
KubernetesCluster: <clustername-here>
So it's necessary to create EBS volume with that tag by using awscli:
aws ec2 create-volume --size 10 --region eu-central-1 --availability-zone eu-central-1a --volume-type gp2 --tag-specifications 'ResourceType=volume,Tags=[{Key=KubernetesCluster,Value=<clustername-here>}]'
or you can tag it by manually in EC2 -> Volumes -> Your volume -> Tags
That's it.
EDIT:
The right cluster name can be found within EC2 instances tags which are part of cluster. Key is the same: KubernetesCluster.
How to add custom logs to CloudWatch? Defaults logs are sent but how to add a custom one?
I already added a file like this: (in .ebextensions)
files:
"/opt/elasticbeanstalk/tasks/bundlelogs.d/applogs.conf" :
mode: "000755"
owner: root
group: root
content: |
/var/app/current/logs/*
"/opt/elasticbeanstalk/tasks/taillogs.d/cloud-init.conf" :
mode: "000755"
owner: root
group: root
content: |
/var/app/current/logs/*
As I did bundlelogs.d and taillogs.d these custom logs are now tailed or retrieved from the console or web, that's nice but they don't persist and are not sent on CloudWatch.
In CloudWatch I have the defaults logs like
/aws/elasticbeanstalk/InstanceName/var/log/eb-activity.log
And I want to have another one like this
/aws/elasticbeanstalk/InstanceName/var/app/current/logs/mycustomlog.log
Both bundlelogs.d and taillogs.d are logs retrieved from management console. What you want to do is extend default logs (e.g. eb-activity.log) to CloudWatch Logs. In order to extend the log stream, you need to add another configuration under /etc/awslogs/config/. The configuration should follow the Agent Configuration file Format.
I've successfully extended my logs for my custom ubuntu/nginx/php platform. Here is my extension file FYI. Here is an official sample FYI.
In your case, it could be like
files:
"/etc/awslogs/config/my_app_log.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/app/current/logs/xxx.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/app/current/logs/xxx.log"]]}`
log_stream_name = {instance_id}
file = /var/app/current/logs/xxx.log*
Credits where due go to Sebastian Hsu and Abhyudit Jain.
This is the final config file I came up with for .ebextensions for our particular use case. Notes explaining some aspects are below the code block.
files:
"/etc/awslogs/config/beanstalklogs_custom.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/log/tomcat8/catalina.out]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Fn::Select" : [ "1", { "Fn::Split" : [ "-", { "Ref":"AWSEBEnvironmentName" } ] } ] }, "var/log/tomcat8/catalina.out"]]}`
log_stream_name = `{"Fn::Join":["--", [{ "Ref":"AWSEBEnvironmentName" }, "{instance_id}"]]}`
file = /var/log/tomcat8/catalina.out*
services:
sysvinit:
awslogs:
files:
- "/etc/awslogs/config/beanstalklogs_custom.conf"
commands:
rm_beanstalklogs_custom_bak:
command: "rm beanstalklogs_custom.conf.bak"
cwd: "/etc/awslogs/config"
ignoreErrors: true
log_group_name
We have a standard naming scheme for our EB environments which is exactly environmentName-environmentType. I'm using { "Fn::Split" : [ "-", { "Ref":"AWSEBEnvironmentName" } ] } to split that into an array of two strings (name and type).
Then I use { "Fn::Select" : [ "1", <<SPLIT_OUTPUT>> ] } to get just the type string. Your needs would obviously differ, so you may only need the following:
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/log/tomcat8/catalina.out"]]}`
log_stream_name
I'm using the Fn::Join function to join the EB environment name with the instance ID. Note that the instance ID template is a string that gets echoed exactly as given.
services
The awslogs service is restarted automatically when the custom conf file is deployed.
commands
When the files block overwrites an existing file, it creates a backup file, like beanstalklogs_custom.conf.bak. This block erases that backup file because awslogs service reads both files, potentially causing conflict.
Result
If you log in to an EC2 instance and sudo cat the file, you should see something like this. Note that all the Fn functions have resolved. If you find that an Fn function didn't resolve, check it for syntax errors.
[/var/log/tomcat8/catalina.out]
log_group_name = /aws/elasticbeanstalk/environmentType/var/log/tomcat8/catalina.out
log_stream_name = environmentName-environmentType--{instance_id}
file = /var/log/tomcat8/catalina.out*
The awslogs agent looks in the configuration file for the log files which it's supposed to send. There are some defaults in it. You need to edit it and specify the files.
You can check and edit the configuration file located at:
/etc/awslogs/awslogs.conf
Make sure to restart the service:
sudo service awslogs restart
You can specify your own files there and create different groups and what not.
Please refer to the following link and you'll be able to get your logs in no time.
Resources:
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html
Edit:
As you don't want to edit the files on the instance, you can add the relevant code to the .ebextensions folder in the root of your code. For example, this is my 01_cloudwatch.config :
packages:
yum:
awslogs: []
container_commands:
01_get_awscli_conf_file:
command: "aws s3 cp s3://project/awscli.conf /etc/awslogs/awscli.conf"
02_get_awslogs_conf_file:
command: "aws s3 cp s3://project/awslogs.conf.${NODE_ENV} /etc/awslogs/awslogs.conf"
03_restart_awslogs:
command: "sudo service awslogs restart"
04_start_awslogs_at_system_boot:
command: "sudo chkconfig awslogs on"
In this config, I am fetching the appropriate config file from a S3 bucket depending on the NODE_ENV. You can do anything you want in your config.
Some great answers already here.
I've detailed in a new Medium blog how this all works and an example .ebextensions file and where to put it.
Below is an excerpt that you might be able to use, the article explains how to determine the right folder/file(s) to stream.
Note that if /var/app/current/logs/* contains many different files this may not work,e.g. if you have
database.log
app.log
random.log
Then you should consider adding a stream for each, however if you have
app.2021-10-18.log
app.2021-10-17.log
app.2021-10-16.log
Then you can use /var/app/current/logs/app.*
packages:
yum:
awslogs: []
option_settings:
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: StreamLogs
value: true
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: DeleteOnTerminate
value: false
- namespace: aws:elasticbeanstalk:cloudwatch:logs
option_name: RetentionInDays
value: 90
files:
"/etc/awslogs/awscli.conf" :
mode: "000600"
owner: root
group: root
content: |
[plugins]
cwlogs = cwlogs
[default]
region = `{"Ref":"AWS::Region"}`
"/etc/awslogs/config/logs.conf" :
mode: "000600"
owner: root
group: root
content: |
[/var/app/current/logs]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "/var/app/current/logs"]]}`
log_stream_name = {instance_id}
file = /var/app/current/logs/*
commands:
"01":
command: systemctl enable awslogsd.service
"02":
command: systemctl restart awslogsd
Looking at the AWS docs it's not immediately apparent, but there are a few things you need to do.
(Our environment is an Amazon Linux AMI - Rails App on the Ruby 2.6 Puma Platform).
First, create a Policy in IAM to give your EB generated EC2 instances access to work with CloudWatch log groups and stream to them - we named ours "EB-Cloudwatch-LogStream-Access".
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/elasticbeanstalk/*:log-stream:*"
}
]
}
Once you have created this, make sure the policy is attached (in IAM > Roles) to your IAM Instance Profile and Service Role that are associated with your EB environment (check the environment's configuration page: Configuration > Security > IAM instance profile | Service Role).
Then, provide a .config file in your .ebextensions directory such as setup_stream_to_cloudwatch.config or 0x_setup_stream_to_cloudwatch.config. In our project we have made it the last extension .config file to run during our deploys by setting a high number for 0x (eg. 09_setup_stream_to_cloudwatch.config).
Then, provide the following, replacing your_log_file with the appropriate filename, keeping in mind that some log files live in /var/log on an Amazon Linux AMI and some (such as those generated by your application) may live in a path such as /var/app/current/log:
files:
'/etc/awslogs/config/logs.conf':
mode: '000600'
owner: root
group: root
content: |
[/var/app/current/log/your_log_file.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/app/current/log/your_log_file.log"]]}`
log_stream_name = {instance_id}
file = /var/app/current/log/your_log_file.log*
commands:
"01":
command: chkconfig awslogs on
"02":
command: service awslogs restart # note that this works for Amazon Linux AMI only - other Linux instances likely use `systemd`
Deploy your application, and you should be set!