-
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 1 commit
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,6 +46,36 @@ 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. | ||
| The job logs feature is implemented only for Kubernetes and is not available for Docker. | ||
|
|
||
| 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 | ||
|
|
||
| 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` | ||
|
|
||
|
wvengen marked this conversation as resolved.
Outdated
|
||
| **Important**: if you used the `compression_method` parameter, the extension of the log file then have two extensions. The | ||
| first extension is `.log` and the second extension is extracted to the label `org.scrapy.extension`. | ||
|
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. The main motivation for the current issue, is that when the config parameter So I would rather add a
Collaborator
Author
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 I follow your logic, it would be better to have two labels: extension which is
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. (previous comment deleted) - yes!
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. and I don't think we'd need the extension label then
Collaborator
Author
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. why we wouldn't need the extension label? how then a person should assume the final destination without knowing if the file was or was not compressed? I mean it is possible to check the config but since we may rollout update the managing pod and put a new config that has a different compression method or do not use compression at all, it would be pretty hard to trace back the extension of the file. I can entirely drop it if you like, just concerned if it makes the life of the user harder since we can reconfigure things without killing the jobs that sit on the cluster in the completed state. it is not possible to provide extension as
Collaborator
Author
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. It is not convenient to return a compression method since it is a separate class and we have a compression extension, it would force us to have a mapping in a log handler class which is not its responsibility, I would still vore for putting the extension in the label and not the method. For the user converting one to another or vice versa is the same amount of mental load. Since we already have the final destination it would be cleaner to use the given info. But if it's important I can add a mapping there.
Collaborator
Author
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. For now I add two labels: extension and compression (compression extension, not method) but as I mentioned if this is something must be (method instead of the extension) then we can add some code, although I do not think it is a clean code
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. I now I understand that my request for adding a compression method label instead of extension, would make the code more complex. I need to look further into this to understand it better, and see if there could be another way. Thanks for explaining!
Collaborator
Author
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 you are concerned that we won't be able to decompress a file with "a quasi random compression extension", there are python packages like https://github.com/pycompression/xopen that automatically assume the compression method from the extension of the file and decompress it.
Collaborator
Author
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 are other technical concerns, feel free to share, I can search for possible solutions. |
||
|
|
||
| For example, if you used | ||
| `compression_method = gzip`, the log file will be named as | ||
| * logs/`org.scrapy.project`/`org.scrapy.spider`/`org.scrapy.job_id`.log.`org.scrapy.extension` | ||
|
wvengen marked this conversation as resolved.
Outdated
|
||
|
|
||
| If you haven't specified a compression method, then none was applied and the final location of the log file is | ||
| * logs/`org.scrapy.project`/`org.scrapy.spider`/`org.scrapy.job_id`.`org.scrapy.extension` | ||
|
|
||
| The joblogs section contains the following parameters: | ||
|
wvengen marked this conversation as resolved.
Outdated
|
||
|
|
||
| * `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`. | ||
|
|
||
|
|
||
| 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,62 @@ 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 extension as label to the pod. | ||
| Adds label that the log file was uploaded. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pod : V1Pod | ||
| The Kubernetes pod object. | ||
| object_name : str | ||
| The object name in storage. | ||
|
|
||
| Returns | ||
| ------- | ||
| bool | ||
| True if the label was added successfully, False otherwise. | ||
| """ | ||
| try: | ||
| extension = os.path.splitext(object_name)[1][1:] | ||
| v1 = client.CoreV1Api() | ||
|
|
||
| # Create a body for the patch operation | ||
| patch_extension = { | ||
| "metadata": { | ||
| "labels": { | ||
| "org.scrapy.log_file_extension": extension | ||
| } | ||
| } | ||
| } | ||
|
|
||
| patch_upload_label = { | ||
| "metadata": { | ||
| "labels": { | ||
| "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 |
||
| } | ||
| } | ||
| } | ||
|
|
||
| # Patch the pod with the new label | ||
| v1.patch_namespaced_pod( | ||
| name=pod.metadata.name, | ||
| namespace=self.namespace, | ||
| body=patch_extension | ||
| ) | ||
| v1.patch_namespaced_pod( | ||
| name=pod.metadata.name, | ||
| namespace=self.namespace, | ||
| body=patch_upload_label | ||
| ) | ||
|
|
||
| logger.info(f"Added log_file_extension labels '{extension}' and log_file_uploaded: 'true' to pod '{pod.metadata.name}'") | ||
| 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 +378,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.