Skip to content

Commit 393bc87

Browse files
carstenwindlerpeppeocchi
authored andcommitted
Feature add psr3 support (#4)
* Adding Psr\Logger support * add documentation * small documentation fix
1 parent 49d1488 commit 393bc87

5 files changed

Lines changed: 256 additions & 4 deletions

File tree

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,37 @@ If you want to send the output to an email address, you need to send first the o
149149
You can pass an array of files or emails if you want to send the output to multiple files/emails
150150
-> `output(['first_file', 'second_file'])->email(['myemail1' => 'Dev1', 'myemail2' => 'Dev2'])`
151151

152+
### Advanced logging
153+
Additionally to the file or email output, you can use a PSR-3 compatible Logger (e.g. Monolog) to handle the job output.
154+
155+
```php
156+
<?php
157+
158+
require_once __DIR__ . '/../vendor/autoload.php';
159+
160+
use GO\Scheduler;
161+
use Monolog\Logger;
162+
use Monolog\Handler\StreamHandler;
163+
164+
$log = new Logger('logger-name');
165+
$log->pushHandler(new StreamHandler('/tmp/log.log', Logger::INFO));
166+
167+
$scheduler
168+
->call(function () {
169+
return "just output something";
170+
})
171+
->at('* * * * *')
172+
->setLogger($log);
173+
174+
$scheduler->run();
175+
```
176+
177+
The Scheduler will use the INFO level for logging the output.
178+
179+
#### More logging options
180+
- `->setLabel('my-log-label')` will log the job output using this label
181+
- `->setJobDoneMessage('job xyz done!')` will add an additional message that will be logged when the job is done. This can be useful if you want to track that a job is executed, even though it does not output anything by itself.
182+
152183
### Conditional
153184
You can delegate the execution of a cronjob to a truthful test.
154185
```

composer.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@
77
{
88
"name": "Giuseppe Occhipinti",
99
"email": "peppeocchi@gmail.com"
10+
},
11+
{
12+
"name": "Carsten Windler",
13+
"email": "carsten@carstenwindler.de",
14+
"homepage": "http://carstenwindler.de",
15+
"role": "Contributor"
1016
}
1117
],
1218
"minimum-stability": "dev",
1319
"require": {
1420
"php": ">=5.5.9",
1521
"mtdowling/cron-expression": "@stable",
16-
"swiftmailer/swiftmailer": "@stable"
22+
"swiftmailer/swiftmailer": "@stable",
23+
"psr/log": "~1.0"
1724
},
1825
"require-dev": {
1926
"phpunit/phpunit": "5.0.*"

src/GO/Job/Job.php

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
use GO\Services\Interval;
55

66
use Cron\CronExpression;
7+
use Psr\Log\LoggerAwareInterface;
8+
use Psr\Log\LoggerInterface;
79
use Swift_Attachment;
810
use Swift_Mailer;
911
use Swift_MailTransport;
1012
use Swift_Message;
1113

12-
abstract class Job
14+
abstract class Job implements LoggerAwareInterface
1315
{
1416
/**
1517
* The command
@@ -80,7 +82,24 @@ abstract class Job
8082
* @var bool
8183
*/
8284
public $truthTest = true;
85+
86+
/**
87+
* PSR-3 compliant logger
88+
* @var \Psr\Log\LoggerInterface
89+
*/
90+
private $logger;
91+
92+
/**
93+
* Label for the job, to make it easier to identify in the logs
94+
* @var string
95+
*/
96+
private $jobLabel;
8397

98+
/**
99+
* Optional message that will be added to the
100+
* @var string
101+
*/
102+
private $jobDoneMessage;
84103

85104
/**
86105
* Create a new instance of Job
@@ -237,7 +256,11 @@ protected function compile($command)
237256
}
238257
}
239258

240-
$command .= ' > /dev/null 2>&1';
259+
// Only hide output if no loggers are used
260+
if (!$this->logger) {
261+
$command .= ' > /dev/null 2>&1';
262+
}
263+
241264
if ($this->runInBackground === true) {
242265
$command .= ' &';
243266
}
@@ -252,16 +275,23 @@ protected function compile($command)
252275
*/
253276
public function exec()
254277
{
278+
$jobOutput = [];
255279
$this->compiled = $this->build();
256280
if (is_callable($this->compiled)) {
257281
$return = call_user_func($this->command, $this->args);
258282
foreach ($this->outputs as $output) {
259283
Filesystem::write($return, $output, $this->mode);
260284
}
285+
286+
if (is_string($return)) {
287+
$jobOutput[] = $return;
288+
}
261289
} else {
262-
$return = exec($this->compiled);
290+
$return = exec($this->compiled, $jobOutput);
263291
}
264292

293+
$this->logJobOutput($jobOutput);
294+
265295
if ($this->emails) {
266296
$this->sendEmails();
267297
}
@@ -329,4 +359,73 @@ public function when($test)
329359

330360
return $this;
331361
}
362+
363+
/**
364+
* Attach a PSR-3 compliant logger to this job
365+
*
366+
* @param \Psr\Log\LoggerInterface $logger
367+
*
368+
* @return $this
369+
*/
370+
public function setLogger(LoggerInterface $logger)
371+
{
372+
$this->logger = $logger;
373+
374+
return $this;
375+
}
376+
377+
/**
378+
* Define a label for this job, which is used by the logger
379+
*
380+
* @param string $label
381+
*
382+
* @return $this
383+
*/
384+
public function setLabel($label)
385+
{
386+
$this->jobLabel = $label;
387+
388+
return $this;
389+
}
390+
391+
/**
392+
* Define a message that will be logged after the job has run
393+
*
394+
* @param string $message
395+
*
396+
* @return $this
397+
*/
398+
public function setJobDoneMessage($message)
399+
{
400+
$this->jobDoneMessage = $message;
401+
402+
return $this;
403+
}
404+
405+
/**
406+
* Get the log label for this job
407+
* @return string
408+
*/
409+
protected function getLogLabel()
410+
{
411+
return (!empty($this->jobLabel)) ? $this->jobLabel : '';
412+
}
413+
414+
/**
415+
* Log output, if there is anything to log
416+
*
417+
* @param array $jobOutput
418+
*
419+
* @return void
420+
*/
421+
protected function logJobOutput($jobOutput)
422+
{
423+
if (count($jobOutput) > 0) {
424+
$this->logger->info($this->getLogLabel(), $jobOutput);
425+
}
426+
427+
if ($this->jobDoneMessage !== null) {
428+
$this->logger->info($this->getLogLabel(), [ $this->jobDoneMessage ]);
429+
}
430+
}
332431
}

tests/GO/Job/LoggerTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php namespace GO\Job\Tests;
2+
3+
use GO\Job\JobFactory;
4+
5+
class JobMock extends \GO\Job\Job
6+
{
7+
public function build()
8+
{
9+
return "dude";
10+
}
11+
}
12+
13+
class LoggerTest extends \PHPUnit_Framework_TestCase
14+
{
15+
private function isWindows()
16+
{
17+
return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
18+
}
19+
20+
private function getPsrLoggerMock()
21+
{
22+
return $this
23+
->getMockBuilder('Psr\Log\LoggerInterface')
24+
->setMethods([ 'emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug', 'log' ])
25+
->getMock();
26+
}
27+
28+
public function testShouldNotSendOutputToDevNullIfLoggerIsAttached()
29+
{
30+
$job = JobFactory::factory('GO\Job\Raw', 'somecommand');
31+
32+
$loggerMock = $this->getMock('Psr\Log\LoggerInterface');
33+
34+
$job->setLogger($loggerMock);
35+
36+
$this->assertEquals('somecommand &', $job->build());
37+
}
38+
39+
public function testShouldLogShellOutput()
40+
{
41+
if ($this->isWindows()) {
42+
$this->markTestSkipped("Can't execute shell script on Windows");
43+
}
44+
45+
$job = JobFactory::factory('GO\Job\Raw', __DIR__ . '/../../test_job.sh');
46+
47+
$loggerMock = $this->getPsrLoggerMock();
48+
$loggerMock
49+
->expects($this->once())
50+
->method('info')
51+
->with('', [ 'testoutput1', 'testoutput2' ]);
52+
53+
$job->setLogger($loggerMock);
54+
55+
$job->exec();
56+
}
57+
58+
public function testShouldLogClosureOutput()
59+
{
60+
$job = JobFactory::factory('GO\Job\Closure', function () {
61+
return "closureoutput";
62+
});
63+
64+
$loggerMock = $this->getPsrLoggerMock();
65+
$loggerMock
66+
->expects($this->once())
67+
->method('info')
68+
->with('', [ 'closureoutput' ]);
69+
70+
$job->setLogger($loggerMock);
71+
72+
$job->exec();
73+
}
74+
75+
public function testShouldLogLabelIfSet()
76+
{
77+
$job = JobFactory::factory('GO\Job\Closure', function () {
78+
return "closureoutput";
79+
});
80+
81+
$loggerMock = $this->getPsrLoggerMock();
82+
$loggerMock
83+
->expects($this->once())
84+
->method('info')
85+
->with('mylabel', [ 'closureoutput' ]);
86+
87+
$job->setLabel('mylabel')->setLogger($loggerMock);
88+
89+
$job->exec();
90+
}
91+
92+
public function testShouldLogJobDoneMessage()
93+
{
94+
$job = JobFactory::factory('GO\Job\Closure', function () {
95+
return "closureoutput";
96+
});
97+
98+
$loggerMock = $this->getPsrLoggerMock();
99+
$loggerMock
100+
->expects($this->at(0))
101+
->method('info')
102+
->with('', [ 'closureoutput' ]);
103+
$loggerMock
104+
->expects($this->at(1))
105+
->method('info')
106+
->with('', [ 'job done' ]);
107+
108+
$job->setJobDoneMessage('job done')->setLogger($loggerMock);
109+
110+
$job->exec();
111+
}
112+
}

tests/test_job.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
echo "testoutput1"
3+
echo "testoutput2"

0 commit comments

Comments
 (0)