def _iter_status_items(self): """Iterator over status information dicts extracted from files""" for job_spec_file in self.files: pid_file = job_spec_file.pid_file if pid_file.exists(): process_exists = pid_file.process.exists() if process_exists is None: error = PIDAccessError( "{} No access to PID {}!".format( pid_file.name, pid_file.pid ) ) self.logger.warning(format_exception(error)) continue if process_exists: status = JobSpecStatus.ACTIVE else: status = JobSpecStatus.STALLED else: status = JobSpecStatus.STALLED item = OrderedDict() item["name"] = job_spec_file.name item["status"] = status item["job_spec"] = job_spec_file.job_spec item["_job_spec_file"] = job_spec_file yield item
def warning(self, exception, silent=False): """Handles exception as warning""" message = format_exception(exception) if not silent: self.logger.warning(message) system_name = platform.node() self.slack.post("[{host}] {message}".format(host=system_name, message=message)) return message + "\n" # to be printed on stdout
def on_error(self, error): """Method is used to notify Cronitor just after an exception is caught on cron scheduler `run`. Works if `CRONMAN_CRON_SCHEDULER_CRONITOR_ID` setting is provided, else does nothing. """ if self.cronitor_id: message = format_exception(error) self.logger.error(message) self.cronitor.fail(self.cronitor_id, msg=message)
def on_error(self, job_spec, cron_job_class, args, kwargs, error): """Notifies monitor services when job failed""" cronitor_id = self.cronitor_id or cron_job_class.cronitor_id message = format_exception(error) self.logger.error(message) self.sentry.capture_exception(error) if cronitor_id and cron_job_class.cronitor_ping_fail: self.cronitor.fail(cronitor_id, msg=message) if self.debug: traceback.print_exc(file=sys.stderr)
def _ping(self, cronitor_id, end_point, msg): """Sends a request to Cronitor web API""" if not self.enabled: self.logger.warning( "Cronitor request ignored (disabled in settings).") return url = self.url.format(cronitor_id=cronitor_id, end_point=end_point) params = {"msg": msg} if msg else None try: response = requests.head(url, params=params, timeout=10) response.raise_for_status() except requests.RequestException as error: # Catch all network and HTTP errors raised by `requests` self.logger.warning("Cronitor request failed: {} {}".format( url, format_exception(error)))
def parse_job_spec_with_class(job_spec=None): """Converts a string (job spec) into quadruple (name, args, kwargs, cron_job_class). Raises CronWorkerInvalidParams if string cannot be parsed or there is no cron job class for given specification. """ if job_spec: try: name, args, kwargs = parse_job_spec(job_spec) cron_job_class = cron_job_registry.get(name) except (ValueError, CronJobNotRegistered) as error: raise CronWorkerInvalidParams(format_exception(error)) else: name = args = kwargs = cron_job_class = None return name, args, kwargs, cron_job_class
def parse_job_spec_or_pid(job_spec_or_pid=None): """Converts a string (job spec or integer - PID) into quadruple (name, args, kwargs, pid). Raises CronWorkerInvalidParams if string cannot be parsed. """ if job_spec_or_pid: if re.match(r"\d+", job_spec_or_pid): pid = int(job_spec_or_pid) name = args = kwargs = None else: pid = None try: name, args, kwargs = parse_job_spec(job_spec_or_pid) except ValueError as error: raise CronWorkerInvalidParams(format_exception(error)) else: name = args = kwargs = pid = None return name, args, kwargs, pid
def post(self, message, channel=None): """Posts a message to Slack channel""" if not self.enabled: self.logger.warning( "Slack request ignored (disabled in settings).") return if not (self.url and self.token): raise ImproperlyConfigured( "CRONMAN_SLACK_URL and CRONMAN_SLACK_TOKEN are required by " "Slack integration. Please provide values for these settings " "or disable Slack integration (CRONMAN_SLACK_ENABLED = False)." ) channel_url = "{}?{}".format( self.url, urlencode(( ("token", self.token), ("channel", "#{}".format(channel or self.default_channel)), )), ) # prepare message message = self._prepare_message(message) try: # We need to split message into packages because of # openssl/urllib3 error. We are going for 12000 chars here # as tests showed that this is properly sent to Slack chunked_message = chunks(message, 12000) for chunk in chunked_message: # Sending data of type unicode behaves badly: # it'll get automatically encoded to some random encoding # you don't get to choose. All data should be encoded # manually by the user before it's passed to requests. encoded_chunk = chunk.encode("utf-8") response = requests.post(channel_url, data=encoded_chunk, timeout=7) response.raise_for_status() except Exception as error: # Catch all network and HTTP errors raised by `requests` self.logger.error("Slack request failed: {}".format( format_exception(error)))
def _iter_status_items(self): """Iterator over status information dicts extracted from files""" for pid_file in self.files: pid = pid_file.pid process_exists = pid_file.process.exists() if process_exists is None: error = PIDAccessError( "{} No access to PID {}!".format(pid_file.name, pid) ) self.logger.warning(format_exception(error)) continue if process_exists: status = PIDStatus.ALIVE else: status = PIDStatus.DEAD item = OrderedDict() item["name"] = pid_file.name item["status"] = status item["pid"] = pid item["_pid_file"] = pid_file yield item