amazon-web-servicesamazon-cloudwatchaws-cloudwatch-log-insights

Generate AWS `Logs Insights` URL with query and search creteria


I'd like to generate a URL for AWS Cloud Watch Logs Insights page where I can customize the following parameters:

  1. The query string
  2. The log groups I'd like to search with-in
  3. The time range

enter image description here

I couldn't find any official documentation for the structure of the URL. Also no API (at least not in boto3) would help me in this.

Here is an example:

https://eu-west-1.console.aws.amazon.com/cloudwatch/home?region=eu-west-1#logsV2:logs-insights$3FqueryDetail$3D$257E$2528end$257E$25272021-07-18T20*3a59*3a59.000Z$257Estart$257E$25272021-07-15T21*3a00*3a00.000Z$257EtimeType$257E$2527ABSOLUTE$257Etz$257E$2527Local$257EeditorString$257E$2527fields*20*40timestamp*2c*20*40message*0a*7c*20sort*20*40timestamp*20desc*0a*7c*20filter*20*40message*20*3d*7e*20*22Exception*22*0a*7c*20limit*20200$257EisLiveTail$257Efalse$257EqueryId$257E$2527####$257Esource$257E$2528$257E$2527*2faws*2flambda*2f######$2529$2529$26tab$3Dlogs

What is the encoding used to generate the URL above?

I'm thinking about simply replace the strings above with the desired params, any better way to achieve this ?


Solution

  • Edit:

    A clean implementation in TypeScript available here written by Danylo Fedorov, Thanks!


    Replacing the strings in the URL isn't that direct, I did the following in Python that will generate link for the logs insights page:

    
    def generate_cloudwatch_url(log_groups, query):
        def escape(s):
            for c in s:
                if c.isalpha() or c.isdigit() or c in ["-", "."]:
                    continue
                c_hex = "*{0:02x}".format(ord(c))
                s = s.replace(c, c_hex)
            return s
    
        def gen_log_insights_url(params):
            S1 = "$257E"
            S2 = "$2528"
            S3 = "$2527"
            S4 = "$2529"
    
            res = f"{S1}{S2}"
            for k in params:
                value = params[k]
                if isinstance(value, str):
                    value = escape(value)
                elif isinstance(value, list):
                    for i in range(len(value)):
                        value[i] = escape(value[i])
                prefix = S1 if list(params.items())[0][0] != k else ""
                suffix = f"{S1}{S3}"
                if isinstance(value, list):
                    value = "".join([f"{S1}{S3}{n}" for n in value])
                    suffix = f"{S1}{S2}"
                elif isinstance(value, int) or isinstance(value, bool):
                    value = str(value).lower()
                    suffix = S1
    
                res += f"{prefix}{k}{suffix}{value}"
            res += f"{S4}{S4}"
            QUERY = f"logsV2:logs-insights$3Ftab$3Dlogs$26queryDetail$3D{res}"
            return f"https://eu-west-1.console.aws.amazon.com/cloudwatch/home?region=eu-west-1#{QUERY}"
        query_vars = {"sample":"value"}
        query = "\n".join([f'| filter {k}="{v}"' for (k, v) in query_vars.items()])
        params = {
            "end": 0,  # "2021-07-18T20:00:00.000Z",
            "start": -60 * 60 * 24,  # "2021-07-15T21:00:00.000Z",
            "unit": "seconds",
            "timeType": "RELATIVE",  # "ABSOLUTE",  # OR RELATIVE and end = 0 and start is negative  seconds
            "tz": "Local",  # OR "UTC"
            "editorString": f"fields @timestamp, @message\n| sort @timestamp desc\n{query}\n| limit 200",
            "isLiveTail": False,
            "source": [f"/aws/lambda/{lg}" for lg in log_groups],
        }
        return gen_log_insights_url(params)
    

    I know it's aweful implementation, but it works!