# Google Cloud Functions

> Monitor Google Cloud Functions using OpenTelemetry instrumentation with Last9

Source: https://last9.io/docs/integrations/cloud-providers/google-cloud-functions/

Use OpenTelemetry to instrument your Google Cloud Function and send telemetry data to Last9.

You can develop and test functions locally using Functions Framework. Developing a function locally can help you test your code without having to rebuild your function container. This can save time and make it easier to test your function.

For more details refer the [link](https://cloud.google.com/run/docs/local-dev-functions#python).

## Prerequisites

Before setting up Google Cloud Functions monitoring, ensure you have:

- Google Cloud account with Cloud Functions enabled
- Python 3.8+ for function runtime
- Last9 account with integration credentials

1. **Install the functions-framework**

   Install the required packages:

   ```bash
   pip install functions-framework opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-distro
   ```

   After installing the required packages, run the following command to install the required instrumentation packages for your application:

   ```bash
   opentelemetry-bootstrap -a install
   ```

   You can install these packages one by one or add them to `requirements.txt` and run `pip install -r requirements.txt`.

2. **Instrument the Python Application**

   Create a new file `main.py` and add the following code:

   ```python
   import asyncio
   import functions_framework
   from flask import Request, jsonify
   from opentelemetry import trace
   from opentelemetry.sdk.trace import TracerProvider
   from opentelemetry.sdk.trace.export import BatchSpanProcessor
   from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
   from opentelemetry.sdk.resources import Resource
   from opentelemetry.semconv.resource import ResourceAttributes
   from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor

   # Initialize OpenTelemetry components
   def initialize_tracing():
       # Create a resource with service information
       resource = Resource.create({
           ResourceAttributes.SERVICE_NAME: "cloud-function", # Replace this with your service name
           ResourceAttributes.SERVICE_VERSION: "0.1.0",
           ResourceAttributes.DEPLOYMENT_ENVIRONMENT: "dev"  # Replace with environment
       })

       # Set up tracer provider with the resource
       provider = TracerProvider(resource=resource)
       trace.set_tracer_provider(provider)

       # Configure the OTLP exporter
       otlp_exporter = OTLPSpanExporter(
           endpoint="{{ .Logs.WriteURL }}",
           headers={
               "authorization":"{{ .Logs.AuthValue }}",
           }
       )

       # For debugging
       # span_processor = BatchSpanProcessor(ConsoleSpanExporter())
       span_processor = BatchSpanProcessor((otlp_exporter))
       provider.add_span_processor(span_processor)

       AsyncioInstrumentor().instrument()

       return trace.get_tracer(__name__)

   tracer = initialize_tracing()

   async def process_request(request_data):
       with tracer.start_as_current_span("process_request") as span:
           span.set_attribute("request.data_size", len(str(request_data)))

           await asyncio.sleep(1)

           result = {"message": "Processed asynchronously", "data": request_data}
           return result

   @functions_framework.http
   def http_handler(request: Request):
       with tracer.start_as_current_span("http_handler", kind=trace.SpanKind.SERVER) as span:
           span.set_attribute("http.method", request.method)
           span.set_attribute("http.url", request.url)
           span.set_attribute("http.route", request.path)

           if request.is_json:
               request_data = request.get_json()
           else:
               request_data = request.form.to_dict() if request.form else request.args.to_dict()

           with tracer.start_as_current_span("extract_headers") as header_span:
               headers = dict(request.headers)
               header_span.set_attribute("request.header_count", len(headers))

               if 'traceparent' in headers:
                   header_span.set_attribute("trace.parent_id", headers['traceparent'])

           try:
               loop = asyncio.new_event_loop()
               asyncio.set_event_loop(loop)
               result = loop.run_until_complete(process_request(request_data))
               loop.close()

               return jsonify(result)
           except Exception as e:
               with tracer.start_as_current_span("error_handler") as error_span:
                   error_span.set_attribute("error.type", str(type(e).__name__))
                   error_span.set_attribute("error.message", str(e))
                   error_span.record_exception(e)

               return jsonify({"error": str(e)}), 500

   # For local testing
   if __name__ == "__main__":
       import os

       # Run the app locally with functions-framework
       # Use: functions-framework --target http_handler --debug
       print("Run with: functions-framework --target http_handler --debug")
   ```

3. **Deploy the Function**

   Refer [this link](https://cloud.google.com/run/docs/deploy-functions#gcloud) to deploy the function on Google Cloud Platform.

## Verification

To verify the setup is working:

1. Test your function locally using Functions Framework
2. Deploy the function to Google Cloud Platform
3. Log into your Last9 account to confirm function traces are being received

## Need Help?

If you encounter any issues or have questions:

- Join our [Discord community](https://discord.com/channels/652153247672729619/652153247672729621) for real-time support
- Contact our support team at [support@last9.io](mailto:support@last9.io)
