A simple framework for emitting metrics from anywhere without an SDK.
Tiny Analytics allows any program with internet access to quickly and easily emit metrics. Think of this like a really simple, self-hosted Google Analytics without the SDK's and other cruft.
At a high level, Tiny Analytics uses http requests to an S3 bucket to generate logs that are then parsed and translated into metrics.
More deeply, this works by sending a specifically formatted http HEAD request to a particular bucket.
This bucket has S3 Access Logs
configured. These logs are then processed by a
Lambda Function which parses the logs, translates them to a metric and then emits that metric
to CloudWatch via
EMF formatted log lines.
- AWS Account
- aws cli installed
Clone this repository by running:
git clone https://github.com/sanjams2/tiny-analytics
cd tiny-analyticsYou first need to deploy the CloudFormation stack to your AWS account. To do this, simply run the following. Note You will need to fill in the following values in the command above:
<STACK NAME>- this will be the name of the CloudFormation stack deployed to your AWS account. It can be anything you like. All additional resources created besides S3 buckets (i.e. lambda funciton) will have this stack name in their name.<METRICS ENDPOINT PREFIX>- this will be the prefix to the url you use for emitting metrics. Example: Specifyingmy-app-metricswill end up generating a metrics endpoint likehttp://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1<CLOUDWATCH METRICS NAMESPACE>- this will be the CloudWatch metrics namespace under which your metrics will be emitted.
aws cloudformation update-stack \
--stack-name <STACK NAME> \
--template-body file://metric-template.yaml \
--parameters ParameterKey=EndpointPrefix,ParameterValue=<METRIC ENDPOINT PREFIX> ParameterKey=MetricNamespace,ParameterValue=<CLOUDWATCH METRIC NAMESPACE> \
--capabilities CAPABILITY_NAMED_IAM \
&& aws cloudformation wait stack-update-complete --stack-name <STACK NAME>Once this completes, you can get the metrics endpoint url by running:
aws cloudformation describe-stacks \
--stack-name <STACK NAME> \
--query "Stacks[0].Outputs[?OutputKey=='MetricsEndpoint'].OutputValue" \
--output textWhich will output something like:
http://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1
The fun part. To emit metrics, you simply need to issue a http HEAD request to the endpoint url specified above.
The url path beyond /metrics/v1 will be name of the metric. Example /metrics/v1/page2view will emit a metric for page2view.
For now, Tiny Analytics only supports count metrics so each call to /metrics/v1/<metric name> emits a count equal to 1 for <metric name>.
Here is an example curl command to emit a metric named page2view for my-app-metrics:
curl -I http://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1/page2viewYou can also add dimensions to the logs emitted by the Lambda that emits metrics. To do this, add query parameter to the url you call. Example using curl:
curl -I http://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1/page2view?pageversion=1.0.2The logs generated by the Lambda to emit this metric will look something like this:
{
"_aws":{
"Timestamp": 1666917984381,
"CloudWatchMetrics": [
{
"Namespace": "MyAppMetricNamespace",
"Dimensions": [[]],
"Metrics": [
{
"Name": "page2view"
}
]
}
]
},
"page2view": 1,
"UserAgent": "curl/7.64.1",
"RequestURI": "HEAD /metrics/v1/page2view?pageversion=1.0.2 HTTP/1.1",
"pageversion": "1.0.2"
}Note that as of now these will not add dimensions to the metrics themselves. This will only add dimensions to the EMF log lines.
import urllib.request
req = urllib.request.Request("http://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1/page2view?pageversion=1.0.2", method="HEAD")
urllib.request.urlopen(req)import "http"
http.Head("http://my-app-metrics.s3.us-west-2.amazonaws.com/metrics/v1/page2view?pageversion=1.0.2")The costs associated with Tiny Analytics are the costs of the AWS resources and interactions with them. These are:
- S3 API Requests
- S3 Data storage
- S3 Data Transfer
- Lambda Invocation Requests
- Lambda Execution costs
- CloudWatch API Requests
- CloudWatch Metric storage
- CloudWatch Log storage
Assuming no free tier usage, for each metric, we can estimate these as the following:
- 1 HEAD object request: $0.0000004
- 1 access log line stored in s3: ($0.023 per GB) * (500B/log line) = $0.0000000115
- 1 GET object request (to read log line): $0.0000004
- 1 Lambda Invoke call: $0.0000002
- 1 Lambda Execution: (250ms/invocation) * ($0.0000000017/ms for 128MB lambda) = $0.00000042
- Data Transfer to Lambda: $0
- 1 PutMetricData call: $0.00001
- 1 PutLogs call: ($0.50 per GB) * (1KB/lambda invocation) = $0.0000005
- Log Storage: ($0.03 per GB) * (1KB/lambda invocation) = $0.00000003
Adding these up we get: $0.0000119615/metric call or $11.96 per 1M metrics. There are some additional costs depending on the number of distinct metrics you emit but it is very minimal (10,000 distinct metrics costs $0.3 a month)
- Support metric dimensions
- Support different metric locations instead of just CloudWatch