Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.00% |
94 / 100 |
|
57.14% |
4 / 7 |
CRAP | |
0.00% |
0 / 1 |
RdsStorage | |
94.00% |
94 / 100 |
|
57.14% |
4 / 7 |
11.03 | |
0.00% |
0 / 1 |
getTableName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
log | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
logMultiple | |
90.00% |
27 / 30 |
|
0.00% |
0 / 1 |
3.01 | |||
searchInstances | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
tableColumns | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
install | |
95.65% |
44 / 46 |
|
0.00% |
0 / 1 |
3 | |||
getInsertChunkSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2016 (original work) Open Assessment Technologies SA; |
19 | * |
20 | * @author Alexander Zagovorichev <zagovorichev@1pt.com> |
21 | */ |
22 | |
23 | namespace oat\taoEventLog\model\eventLog; |
24 | |
25 | use oat\taoEventLog\model\LogEntity; |
26 | use Doctrine\DBAL\Schema\SchemaException; |
27 | use oat\taoEventLog\model\storage\AbstractRdsStorage; |
28 | use Throwable; |
29 | |
30 | /** |
31 | * Class RdsStorage |
32 | * @package oat\taoEventLog\model\storage |
33 | */ |
34 | class RdsStorage extends AbstractRdsStorage |
35 | { |
36 | public const EVENT_LOG_TABLE_NAME = 'event_log'; |
37 | |
38 | public const SERVICE_ID = 'taoEventLog/eventLogStorage'; |
39 | |
40 | public const OPTION_INSERT_CHUNK_SIZE = 'insertChunkSize'; |
41 | |
42 | public const EVENT_LOG_ID = self::ID; |
43 | public const EVENT_LOG_EVENT_NAME = 'event_name'; |
44 | public const EVENT_LOG_ACTION = 'action'; |
45 | public const EVENT_LOG_USER_ID = 'user_id'; |
46 | public const EVENT_LOG_USER_ROLES = 'user_roles'; |
47 | public const EVENT_LOG_OCCURRED = 'occurred'; |
48 | public const EVENT_LOG_PROPERTIES = 'properties'; |
49 | |
50 | private const DEFAULT_INSERT_CHUNK_SIZE = 100; |
51 | |
52 | /** |
53 | * @return string |
54 | */ |
55 | public function getTableName() |
56 | { |
57 | return self::EVENT_LOG_TABLE_NAME; |
58 | } |
59 | |
60 | /** |
61 | * @param LogEntity $logEntity |
62 | * @return bool |
63 | */ |
64 | public function log(LogEntity $logEntity) |
65 | { |
66 | $result = $this->getPersistence()->insert( |
67 | $this->getTableName(), |
68 | [ |
69 | self::EVENT_LOG_EVENT_NAME => $logEntity->getEvent()->getName(), |
70 | self::EVENT_LOG_ACTION => $logEntity->getAction(), |
71 | self::EVENT_LOG_USER_ID => $logEntity->getUser()->getIdentifier(), |
72 | self::EVENT_LOG_USER_ROLES => join(',', $logEntity->getUser()->getRoles()), |
73 | self::EVENT_LOG_OCCURRED => $logEntity->getTime()->format(self::DATE_TIME_FORMAT), |
74 | self::EVENT_LOG_PROPERTIES => json_encode($logEntity->getData()), |
75 | ] |
76 | ); |
77 | |
78 | return $result === 1; |
79 | } |
80 | |
81 | public function logMultiple(LogEntity ...$logEntities): bool |
82 | { |
83 | $inserts = array_map( |
84 | static fn (LogEntity $logEntity): array => [ |
85 | self::EVENT_LOG_EVENT_NAME => $logEntity->getEvent()->getName(), |
86 | self::EVENT_LOG_ACTION => $logEntity->getAction(), |
87 | self::EVENT_LOG_USER_ID => $logEntity->getUser()->getIdentifier(), |
88 | self::EVENT_LOG_USER_ROLES => implode(',', $logEntity->getUser()->getRoles()), |
89 | self::EVENT_LOG_OCCURRED => $logEntity->getTime()->format(self::DATE_TIME_FORMAT), |
90 | self::EVENT_LOG_PROPERTIES => json_encode($logEntity->getData()), |
91 | ], |
92 | $logEntities |
93 | ); |
94 | |
95 | try { |
96 | $persistence = $this->getPersistence(); |
97 | |
98 | $persistence->transactional(function () use ($inserts, $persistence) { |
99 | $insertCount = count($inserts); |
100 | $insertChunkSize = $this->getInsertChunkSize(); |
101 | |
102 | foreach (array_chunk($inserts, $insertChunkSize) as $index => $chunk) { |
103 | $this->logDebug( |
104 | sprintf( |
105 | 'Processing chunk %d/%d with %d log entries', |
106 | $index + 1, |
107 | ceil($insertCount / $insertChunkSize), |
108 | count($chunk) |
109 | ) |
110 | ); |
111 | |
112 | $persistence->insertMultiple($this->getTableName(), $chunk); |
113 | } |
114 | }); |
115 | |
116 | return true; |
117 | } catch (Throwable $exception) { |
118 | $this->logError('Error when inserting log entries: ' . $exception->getMessage()); |
119 | |
120 | return false; |
121 | } |
122 | } |
123 | |
124 | /** |
125 | * @param array $params |
126 | * @deprecated use $this->search() instead |
127 | * @return array |
128 | */ |
129 | public function searchInstances(array $params = []) |
130 | { |
131 | return $this->search($params); |
132 | } |
133 | |
134 | /** |
135 | * @inheritdoc |
136 | */ |
137 | public static function tableColumns() |
138 | { |
139 | return [ |
140 | self::EVENT_LOG_ID, |
141 | self::EVENT_LOG_USER_ID, |
142 | self::EVENT_LOG_USER_ROLES, |
143 | self::EVENT_LOG_EVENT_NAME, |
144 | self::EVENT_LOG_ACTION, |
145 | self::EVENT_LOG_OCCURRED, |
146 | self::EVENT_LOG_PROPERTIES |
147 | ]; |
148 | } |
149 | |
150 | /** |
151 | * @inheritdoc |
152 | */ |
153 | public static function install($persistence) |
154 | { |
155 | /** @var AbstractSchemaManager $schemaManager */ |
156 | $schemaManager = $persistence->getDriver()->getSchemaManager(); |
157 | |
158 | /** @var Schema $schema */ |
159 | $schema = $schemaManager->createSchema(); |
160 | $fromSchema = clone $schema; |
161 | |
162 | try { |
163 | $table = $schema->createTable(self::EVENT_LOG_TABLE_NAME); |
164 | $table->addOption('engine', 'MyISAM'); |
165 | |
166 | $table->addColumn( |
167 | self::EVENT_LOG_ID, |
168 | "integer", |
169 | ["notnull" => true, "autoincrement" => true, 'unsigned' => true] |
170 | ); |
171 | $table->addColumn( |
172 | self::EVENT_LOG_EVENT_NAME, |
173 | "string", |
174 | ["notnull" => true, "length" => 255, 'comment' => 'Event name'] |
175 | ); |
176 | $table->addColumn( |
177 | self::EVENT_LOG_ACTION, |
178 | "string", |
179 | ["notnull" => false, "length" => 1000, 'comment' => 'Current action'] |
180 | ); |
181 | $table->addColumn( |
182 | self::EVENT_LOG_USER_ID, |
183 | "string", |
184 | ["notnull" => false, "length" => 255, 'default' => '', 'comment' => 'User identifier'] |
185 | ); |
186 | $table->addColumn( |
187 | self::EVENT_LOG_USER_ROLES, |
188 | "text", |
189 | ["notnull" => true, 'default' => '', 'comment' => 'User roles'] |
190 | ); |
191 | $table->addColumn(self::EVENT_LOG_OCCURRED, "datetime", ["notnull" => true]); |
192 | $table->addColumn( |
193 | self::EVENT_LOG_PROPERTIES, |
194 | "text", |
195 | ["notnull" => false, 'default' => '', 'comment' => 'Event properties in json'] |
196 | ); |
197 | |
198 | $table->setPrimaryKey([self::EVENT_LOG_ID]); |
199 | $table->addIndex([self::EVENT_LOG_EVENT_NAME], 'idx_event_name'); |
200 | $table->addIndex([self::EVENT_LOG_ACTION], 'idx_action', [], ['lengths' => [164]]); |
201 | $table->addIndex([self::EVENT_LOG_USER_ID], 'idx_user_id'); |
202 | $table->addIndex([self::EVENT_LOG_OCCURRED], 'idx_occurred'); |
203 | } catch (SchemaException $e) { |
204 | \common_Logger::i('Database Schema for EventLog already up to date.'); |
205 | } |
206 | |
207 | $queries = $persistence->getPlatForm()->getMigrateSchemaSql($fromSchema, $schema); |
208 | foreach ($queries as $query) { |
209 | $persistence->exec($query); |
210 | } |
211 | } |
212 | |
213 | private function getInsertChunkSize(): int |
214 | { |
215 | return $this->getOption(self::OPTION_INSERT_CHUNK_SIZE, self::DEFAULT_INSERT_CHUNK_SIZE); |
216 | } |
217 | } |