-
Notifications
You must be signed in to change notification settings - Fork 7
Add joblog label to job #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,9 +46,38 @@ For Kubernetes, it is important to set resource limits. | |
| TODO: explain how to set limits, with default, project and spider specificity. | ||
|
|
||
| ### [joblogs] section | ||
|
|
||
| The joblogs section is used to configure the job logs feature. It is not enabled by default, but can be activated if you | ||
| choose to use it. The job logs feature allows you to collect logs from the Kubernetes cluster and store them in a specified | ||
| directory. The logs are collected from the pods running on the cluster and can be compressed using a specified method. | ||
| If the log file was successfully uploaded to the remote storage, the pod is labeled with `org.scrapy.log_file_uploaded: true`. | ||
| The job logs feature is implemented only for Kubernetes and is not available for Docker. | ||
|
|
||
| The joblogs section contains the following parameters: | ||
|
|
||
| * `logs_dir` - a directory to store log files collected on k8s cluster (implemented only for Kubernetes). If you are using a Persistent Volume, keep in mind, that the provided path should be mounted in the deployment manifest. Read and write permissions should be granted to allow actions with log files in the provided directory, thus a [securityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) was added to the deployment manifest. | ||
| * `compression_method` - a method to compress log files. Available options are `gzip` `bzip2`, `lzma`, `brotli` and `none`. If no compression_method is provided, it defaults to `none`. | ||
|
|
||
| If you would like to provide a `compression_method` parameter, please, read the dedicated section below. | ||
|
|
||
| ### compression for joblogs | ||
|
|
||
| When the log file was uploaded to the specified storage, the pod is labeled with org.scrapy.log_file_uploaded=true, it also | ||
| contains labels to specify an end location of the log files in the storage. | ||
| Since the label field has limitation in the number of symbols, an end location is split into several labels, each of them | ||
| contains a part of the end location. The labels are named as follows: | ||
| * org.scrapy.project | ||
| * org.scrapy.spider | ||
| * org.scrapy.job_id | ||
| * org.scrapy.extension | ||
| * org.scrapy.compression | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we call this |
||
|
|
||
| How can you build the end location from these labels? Complete the following line | ||
| * logs/`org.scrapy.project`/`org.scrapy.spider`/`org.scrapy.job_id`.`org.scrapy.extension`.`org.scrapy.compression` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't |
||
|
|
||
| **Important**: if you did not use the `compression_method` parameter, the label `org.scrapy.compression` has `none` value | ||
| so the final destination of the log file will be: | ||
| * logs/`org.scrapy.project`/`org.scrapy.spider`/`org.scrapy.job_id`.`org.scrapy.extension` | ||
|
|
||
| ### Kubernetes API interaction | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -166,6 +166,7 @@ metadata: | |
| rules: | ||
| - apiGroups: [""] | ||
| resources: ["pods"] | ||
| # add "patch" if you use joblogs feature | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's make it |
||
| verbs: ["get", "list", "watch"] | ||
| - apiGroups: [""] | ||
| resources: ["pods/exec"] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,58 @@ def __init__(self, config): | |
| raise ValueError("Configuration error: 'logs_dir' is missing in joblogs configuration section.") | ||
| self.object_storage_provider = LibcloudObjectStorage(self.config) | ||
|
|
||
| def _add_object_name_label(self, pod, object_name): | ||
| """ | ||
| Adds file metadata as labels to the pod. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pod : V1Pod | ||
| The Kubernetes pod object. | ||
| object_name : str | ||
| The object name in storage. | ||
|
|
||
| Returns | ||
| ------- | ||
| bool | ||
| True if labels were added successfully, False otherwise. | ||
| """ | ||
| try: | ||
| basename = os.path.basename(object_name) | ||
|
|
||
| extension = "none" | ||
| compression = "none" | ||
|
|
||
| if basename.endswith(".log"): | ||
| extension = "log" | ||
| elif ".log." in basename: | ||
| extension = "log" | ||
| compression = basename.rsplit(".log.", 1)[1] | ||
|
|
||
| patch_body = { | ||
| "metadata": { | ||
| "labels": { | ||
| "org.scrapy.extension": extension, | ||
| "org.scrapy.compression": compression, | ||
| "org.scrapy.log_file_uploaded": "true" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there could be multiple uses of object storage, I propose include |
||
| } | ||
| } | ||
| } | ||
|
|
||
| v1 = client.CoreV1Api() | ||
| v1.patch_namespaced_pod( | ||
| name=pod.metadata.name, | ||
| namespace=self.namespace, | ||
| body=patch_body | ||
| ) | ||
|
|
||
| logger.info(f"Added labels to pod '{pod.metadata.name}': extension='{extension}', compression='{compression}', uploaded='true'") | ||
| return True | ||
|
|
||
| except Exception as e: | ||
| logger.error(f"Failed to add labels to pod '{pod.metadata.name}': {e}") | ||
| return False | ||
|
|
||
| def get_existing_log_filename(self, job_id): | ||
| """ | ||
| Retrieves the existing temporary log file path for a job without creating a new one. | ||
|
|
@@ -322,7 +374,12 @@ def handle_events(self, event): | |
| logger.info( | ||
| f"Removed local log file '{log_filename}' since it already exists in storage.") | ||
| else: | ||
| self.object_storage_provider.upload_file(project, spider, log_filename) | ||
| object_name = self.object_storage_provider.upload_file(project, spider, log_filename) | ||
| if object_name: | ||
| try: | ||
| self._add_object_name_label(pod, object_name) | ||
| except Exception as e: | ||
| logger.error(f"Exception while adding label for job '{job_id}': {e}") | ||
| os.remove(log_filename) | ||
| logger.info(f"Removed local log file '{log_filename}' after successful upload.") | ||
| else: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's reference #37 here.