-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocation.py
More file actions
171 lines (146 loc) · 6.77 KB
/
location.py
File metadata and controls
171 lines (146 loc) · 6.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import json
import logging
import requests
import os
import time
from opentelemetry import trace
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
SimpleSpanProcessor,
ConsoleSpanExporter
)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Configure OpenTelemetry
resource = Resource(attributes={
SERVICE_NAME: os.environ.get("AWS_LAMBDA_FUNCTION_NAME")
})
trace.set_tracer_provider(TracerProvider(resource=resource))
# Configure OTLP exporter (AWS X-Ray or other OpenTelemetry backend)
otlp_exporter = OTLPSpanExporter(
endpoint=os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4318/v1/traces")
)
# Add span processor to the tracer provider
trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(otlp_exporter))
# Get a tracer
tracer = trace.get_tracer(__name__)
# Instrument the requests library
RequestsInstrumentor().instrument()
def lambda_handler(event, context):
"""
AWS Lambda function that takes a zip code and returns its latitude and longitude coordinates.
Parameters:
event (dict): Should contain a 'zipcode' key with the zip code to look up
context (LambdaContext): Lambda runtime information
Returns:
dict: Response containing status code, headers, and body with coordinates data
"""
# Create a parent span for the entire lambda execution
with tracer.start_as_current_span("zipcode_lookup_lambda") as parent_span:
parent_span.set_attribute("lambda.name", context.function_name)
parent_span.set_attribute("lambda.request_id", context.aws_request_id)
logger.info(f"Received event: {json.dumps(event)}")
parent_span.add_event("received_event", {"event": json.dumps(event)})
# Check if zipcode is provided in the event
if 'zipcode' not in event:
logger.error("No zipcode parameter provided in the event")
parent_span.set_status(trace.StatusCode.ERROR)
parent_span.record_exception(Exception("Missing zipcode parameter"))
return {
'statusCode': 400,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'error': 'Missing required parameter: zipcode'
})
}
zipcode = event['zipcode']
parent_span.set_attribute("zipcode", zipcode)
logger.info(f"Looking up coordinates for zip code: {zipcode}")
try:
# Create a span for the API call
with tracer.start_as_current_span("zipcode_api_request") as api_span:
# You can use a free service like ZipCodeAPI or similar
# This example uses the public ZipPopatamus API which doesn't require authentication
api_url = f"https://api.zippopotam.us/us/{zipcode}"
api_span.set_attribute("http.method", "GET")
api_span.set_attribute("http.url", api_url)
logger.info(f"Making API request to: {api_url}")
start_time = time.time()
response = requests.get(api_url)
elapsed_time = time.time() - start_time
api_span.set_attribute("http.status_code", response.status_code)
api_span.set_attribute("http.response_time_ms", elapsed_time * 1000)
response.raise_for_status() # Raises an exception for 4XX/5XX responses
data = response.json()
logger.info(f"Received response: {json.dumps(data)}")
# Process the results with a separate span
with tracer.start_as_current_span("process_zipcode_results") as process_span:
# Extract coordinates
if 'places' in data and len(data['places']) > 0:
latitude = data['places'][0]['latitude']
longitude = data['places'][0]['longitude']
place_name = data['places'][0]['place name']
state = data['places'][0]['state']
process_span.set_attribute("location.latitude", latitude)
process_span.set_attribute("location.longitude", longitude)
process_span.set_attribute("location.place", place_name)
process_span.set_attribute("location.state", state)
logger.info(f"Found coordinates: {latitude}, {longitude} for {place_name}, {state}")
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'zipcode': zipcode,
'latitude': latitude,
'longitude': longitude,
'place_name': place_name,
'state': state
})
}
else:
logger.warning(f"No location data found for zip code: {zipcode}")
process_span.set_status(trace.StatusCode.ERROR)
process_span.record_exception(Exception("No location data found"))
return {
'statusCode': 404,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'error': f"No location data found for zip code: {zipcode}"
})
}
except requests.exceptions.HTTPError as e:
logger.error(f"HTTP error occurred: {str(e)}")
parent_span.set_status(trace.StatusCode.ERROR)
parent_span.record_exception(e)
return {
'statusCode': response.status_code,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'error': f"API error: {str(e)}"
})
}
except Exception as e:
logger.error(f"Error occurred: {str(e)}")
parent_span.set_status(trace.StatusCode.ERROR)
parent_span.record_exception(e)
return {
'statusCode': 500,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'error': f"Internal server error: {str(e)}"
})
}