Completed
Push — master ( 15f106...b12154 )
by
unknown
02:36
created

LoggingHelper::logHugeDependencyMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 12
nc 1
nop 2
1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Helper;
4
5
use Config;
6
use IBufferingStatsdDataFactory;
7
use Psr\Log\LoggerInterface;
8
use Wikibase\DataModel\Entity\EntityId;
9
use WikibaseQuality\ConstraintReport\Constraint;
10
use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
11
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
12
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
13
14
/**
15
 * Helper class for tracking and logging messages.
16
 *
17
 * @author Lucas Werkmeister
18
 * @license GNU GPL v2+
19
 */
20
class LoggingHelper {
21
22
	/**
23
	 * @var IBufferingStatsdDataFactory
24
	 */
25
	private $dataFactory;
26
27
	/**
28
	 * @var LoggerInterface
29
	 */
30
	private $logger;
31
32
	/**
33
	 * @var float[]
34
	 */
35
	private $constraintCheckDurationLimits;
36
37
	/**
38
	 * @param IBufferingStatsdDataFactory $dataFactory,
0 ignored issues
show
Documentation introduced by
There is no parameter named $dataFactory,. Did you maybe mean $dataFactory?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
39
	 * @param LoggerInterface $logger
40
	 * @param Config $config
41
	 */
42
	public function __construct(
43
		IBufferingStatsdDataFactory $dataFactory,
44
		LoggerInterface $logger,
45
		Config $config
46
	) {
47
		$this->dataFactory = $dataFactory;
48
		$this->logger = $logger;
49
		$this->constraintCheckDurationLimits = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('info' => $config-...rationWarningSeconds')) of type array<string,?,{"info":"?","warning":"?"}> is incompatible with the declared type array<integer,double> of property $constraintCheckDurationLimits.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
50
			'info' => $config->get( 'WBQualityConstraintsCheckDurationInfoSeconds' ),
51
			'warning' => $config->get( 'WBQualityConstraintsCheckDurationWarningSeconds' ),
52
		];
53
	}
54
55
	/**
56
	 * Log a constraint check.
57
	 * The constraint check is tracked on the statsd data factory,
58
	 * and also logged with the logger interface if it took longer than a certain time.
59
	 * Multiple limits corresponding to different log levels can be specified in the configuration;
60
	 * checks that exceed a higher limit are logged at a more severe level.
61
	 *
62
	 * @param Context $context
63
	 * @param Constraint $constraint
64
	 * @param CheckResult $result
65
	 * @param string $constraintCheckerClass
66
	 * @param float $durationSeconds
67
	 * @param string $method Use __METHOD__.
68
	 */
69
	public function logConstraintCheck(
70
		Context $context,
71
		Constraint $constraint,
72
		CheckResult $result,
73
		$constraintCheckerClass,
74
		$durationSeconds,
75
		$method
76
	) {
77
		$constraintCheckerClassShortName = substr( strrchr( $constraintCheckerClass, '\\' ), 1 );
78
		$constraintTypeItemId = $constraint->getConstraintTypeItemId();
79
80
		$this->dataFactory->timing(
81
			'wikibase.quality.constraints.check.timing.' .
82
				$constraintTypeItemId . '-' .
83
				$constraintCheckerClassShortName,
84
			$durationSeconds * 1000
85
		);
86
87
		// find the longest limit (and associated log level) that the duration exceeds
88
		foreach ( $this->constraintCheckDurationLimits as $level => $limit ) {
89
			if (
90
				// duration exceeds this limit
91
				isset( $limit ) && $durationSeconds > $limit &&
92
				// this limit is longer than previous longest limit
93
				( !isset( $limitSeconds ) || $limit > $limitSeconds )
0 ignored issues
show
Bug introduced by
The variable $limitSeconds does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
94
			) {
95
				$limitSeconds = $limit;
96
				$logLevel = $level;
97
			}
98
		}
99
100
		if ( !isset( $limitSeconds ) ) {
101
			return;
102
		}
103
		if ( $context->getType() !== Context::TYPE_STATEMENT ) {
104
			// TODO log less details but still log something
105
			return;
106
		}
107
108
		$resultMessage = $result->getMessage();
109
		if ( $resultMessage instanceof ViolationMessage ) {
110
			$resultMessage = $resultMessage->getMessageKey();
111
		}
112
113
		$this->logger->log(
114
			$logLevel,
0 ignored issues
show
Bug introduced by
The variable $logLevel does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
115
			'Constraint check with {constraintCheckerClassShortName} ' .
116
			'took longer than {limitSeconds} second(s) ' .
117
			'(duration: {durationSeconds} seconds).',
118
			[
119
				'method' => $method,
120
				'loggingMethod' => __METHOD__,
121
				'durationSeconds' => $durationSeconds,
122
				'limitSeconds' => $limitSeconds,
123
				'constraintId' => $constraint->getConstraintId(),
124
				'constraintPropertyId' => $constraint->getPropertyId()->getSerialization(),
125
				'constraintTypeItemId' => $constraintTypeItemId,
126
				'constraintParameters' => $constraint->getConstraintParameters(),
127
				'constraintCheckerClass' => $constraintCheckerClass,
128
				'constraintCheckerClassShortName' => $constraintCheckerClassShortName,
129
				'entityId' => $context->getEntity()->getId()->getSerialization(),
130
				'statementGuid' => $context->getSnakStatement()->getGuid(),
131
				'resultStatus' => $result->getStatus(),
132
				'resultParameters' => $result->getParameters(),
133
				'resultMessage' => $resultMessage,
134
			]
135
		);
136
	}
137
138
	/**
139
	 * Log a cache hit for a complete constraint check result for the given entity ID.
140
	 *
141
	 * @param EntityId $entityId
142
	 */
143
	public function logCheckConstraintsCacheHit( EntityId $entityId ) {
0 ignored issues
show
Unused Code introduced by
The parameter $entityId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
144
		$this->dataFactory->increment(
145
			'wikibase.quality.constraints.cache.entity.hit'
146
		);
147
	}
148
149
	/**
150
	 * Log cache misses for a complete constraint check result for the given entity IDs.
151
	 *
152
	 * @param EntityId[] $entityIds
153
	 */
154
	public function logCheckConstraintsCacheMisses( array $entityIds ) {
155
		$this->dataFactory->updateCount(
156
			'wikibase.quality.constraints.cache.entity.miss',
157
			count( $entityIds )
158
		);
159
	}
160
161
	/**
162
	 * Log that the dependency metadata for a check result had an empty set of entity IDs.
163
	 * This should never happen – at least the entity being checked should always be contained.
164
	 */
165
	public function logEmptyDependencyMetadata() {
166
		$this->logger->log(
167
			'warning',
168
			'Dependency metadata for constraint check result had empty set of entity IDs.',
169
			[
170
				'loggingMethod' => __METHOD__,
171
				// callers of this method don’t have much information to pass to us,
172
				// so for now we don’t log any other structured data
173
				// and hope that the request URL provides enough information
174
			]
175
		);
176
	}
177
178
	/**
179
	 * Log that the dependency metadata for a check result has a very large set of entity IDs.
180
	 *
181
	 * @param EntityId[] $entityIds
182
	 * @param int $maxRevisionIds
183
	 */
184
	public function logHugeDependencyMetadata( array $entityIds, $maxRevisionIds ) {
185
		$this->logger->log(
186
			'warning',
187
			'Dependency metadata for constraint check result has huge set of entity IDs ' .
188
			'(count ' . count( $entityIds ) . ', limit ' . $maxRevisionIds . '); ' .
189
			'caching disabled for this check result.',
190
			[
191
				'loggingMethod' => __METHOD__,
192
				'entityIds' => array_map(
193
					function ( EntityId $entityId ) {
194
						return $entityId->getSerialization();
195
					},
196
					$entityIds
197
				),
198
				'maxRevisionIds' => $maxRevisionIds,
199
			]
200
		);
201
	}
202
203
}
204