Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/cloud/logging_v2/_gapic.py: 29%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

156 statements  

1# Copyright 2016 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Wrapper for adapting the autogenerated gapic client to the hand-written 

16client.""" 

17 

18from google.cloud.logging_v2.services.config_service_v2 import ConfigServiceV2Client 

19from google.cloud.logging_v2.services.logging_service_v2 import LoggingServiceV2Client 

20from google.cloud.logging_v2.services.metrics_service_v2 import MetricsServiceV2Client 

21from google.cloud.logging_v2.types import CreateSinkRequest 

22from google.cloud.logging_v2.types import UpdateSinkRequest 

23from google.cloud.logging_v2.types import ListSinksRequest 

24from google.cloud.logging_v2.types import ListLogMetricsRequest 

25from google.cloud.logging_v2.types import ListLogEntriesRequest 

26from google.cloud.logging_v2.types import WriteLogEntriesRequest 

27from google.cloud.logging_v2.types import LogSink 

28from google.cloud.logging_v2.types import LogMetric 

29from google.cloud.logging_v2.types import LogEntry as LogEntryPB 

30 

31from google.protobuf.json_format import MessageToDict 

32from google.protobuf.json_format import ParseDict 

33from google.protobuf.json_format import ParseError 

34 

35from google.cloud.logging_v2._helpers import entry_from_resource 

36from google.cloud.logging_v2.sink import Sink 

37from google.cloud.logging_v2.metric import Metric 

38 

39from google.api_core import client_info 

40from google.api_core import gapic_v1 

41 

42 

43class _LoggingAPI(object): 

44 """Helper mapping logging-related APIs.""" 

45 

46 def __init__(self, gapic_api, client): 

47 self._gapic_api = gapic_api 

48 self._client = client 

49 

50 def list_entries( 

51 self, 

52 resource_names, 

53 *, 

54 filter_=None, 

55 order_by=None, 

56 max_results=None, 

57 page_size=None, 

58 page_token=None, 

59 ): 

60 """Return a generator of log entry resources. 

61 

62 Args: 

63 resource_names (Sequence[str]): Names of one or more parent resources 

64 from which to retrieve log entries: 

65 

66 :: 

67 

68 "projects/[PROJECT_ID]" 

69 "organizations/[ORGANIZATION_ID]" 

70 "billingAccounts/[BILLING_ACCOUNT_ID]" 

71 "folders/[FOLDER_ID]" 

72 

73 filter_ (str): a filter expression. See 

74 https://cloud.google.com/logging/docs/view/advanced_filters 

75 order_by (str) One of :data:`~logging_v2.ASCENDING` 

76 or :data:`~logging_v2.DESCENDING`. 

77 max_results (Optional[int]): 

78 Optional. The maximum number of entries to return. 

79 Non-positive values are treated as 0. If None, uses API defaults. 

80 page_size (int): number of entries to fetch in each API call. Although 

81 requests are paged internally, logs are returned by the generator 

82 one at a time. If not passed, defaults to a value set by the API. 

83 page_token (str): opaque marker for the starting "page" of entries. If not 

84 passed, the API will return the first page of entries. 

85 Returns: 

86 Generator[~logging_v2.LogEntry] 

87 """ 

88 # full resource names are expected by the API 

89 resource_names = resource_names 

90 request = ListLogEntriesRequest( 

91 resource_names=resource_names, 

92 filter=filter_, 

93 order_by=order_by, 

94 page_size=page_size, 

95 page_token=page_token, 

96 ) 

97 

98 response = self._gapic_api.list_log_entries(request=request) 

99 log_iter = iter(response) 

100 

101 # We attach a mutable loggers dictionary so that as Logger 

102 # objects are created by entry_from_resource, they can be 

103 # re-used by other log entries from the same logger. 

104 loggers = {} 

105 

106 if max_results is not None and max_results < 0: 

107 raise ValueError("max_results must be positive") 

108 

109 # create generator 

110 def log_entries_pager(log_iter): 

111 i = 0 

112 for entry in log_iter: 

113 if max_results is not None and i >= max_results: 

114 break 

115 log_entry_dict = _parse_log_entry(LogEntryPB.pb(entry)) 

116 yield entry_from_resource(log_entry_dict, self._client, loggers=loggers) 

117 i += 1 

118 

119 return log_entries_pager(log_iter) 

120 

121 def write_entries( 

122 self, 

123 entries, 

124 *, 

125 logger_name=None, 

126 resource=None, 

127 labels=None, 

128 partial_success=True, 

129 dry_run=False, 

130 ): 

131 """Log an entry resource via a POST request 

132 

133 Args: 

134 entries (Sequence[Mapping[str, ...]]): sequence of mappings representing 

135 the log entry resources to log. 

136 logger_name (Optional[str]): name of default logger to which to log the entries; 

137 individual entries may override. 

138 resource(Optional[Mapping[str, ...]]): default resource to associate with entries; 

139 individual entries may override. 

140 labels (Optional[Mapping[str, ...]]): default labels to associate with entries; 

141 individual entries may override. 

142 partial_success (Optional[bool]): Whether valid entries should be written even if 

143 some other entries fail due to INVALID_ARGUMENT or 

144 PERMISSION_DENIED errors. If any entry is not written, then 

145 the response status is the error associated with one of the 

146 failed entries and the response includes error details keyed 

147 by the entries' zero-based index in the ``entries.write`` 

148 method. 

149 dry_run (Optional[bool]): 

150 If true, the request should expect normal response, 

151 but the entries won't be persisted nor exported. 

152 Useful for checking whether the logging API endpoints are working 

153 properly before sending valuable data. 

154 """ 

155 try: 

156 log_entry_pbs = [_log_entry_mapping_to_pb(entry) for entry in entries] 

157 except ParseError as e: 

158 raise ValueError(f"Invalid log entry: {str(e)}") from e 

159 

160 request = WriteLogEntriesRequest( 

161 log_name=logger_name, 

162 resource=resource, 

163 labels=labels, 

164 entries=log_entry_pbs, 

165 partial_success=partial_success, 

166 ) 

167 self._gapic_api.write_log_entries(request=request) 

168 

169 def logger_delete(self, logger_name): 

170 """Delete all entries in a logger. 

171 

172 Args: 

173 logger_name (str): The resource name of the log to delete: 

174 

175 :: 

176 

177 "projects/[PROJECT_ID]/logs/[LOG_ID]" 

178 "organizations/[ORGANIZATION_ID]/logs/[LOG_ID]" 

179 "billingAccounts/[BILLING_ACCOUNT_ID]/logs/[LOG_ID]" 

180 "folders/[FOLDER_ID]/logs/[LOG_ID]" 

181 

182 ``[LOG_ID]`` must be URL-encoded. For example, 

183 ``"projects/my-project-id/logs/syslog"``, 

184 ``"organizations/1234567890/logs/cloudresourcemanager.googleapis.com%2Factivity"``. 

185 """ 

186 self._gapic_api.delete_log(log_name=logger_name) 

187 

188 

189class _SinksAPI(object): 

190 """Helper mapping sink-related APIs.""" 

191 

192 def __init__(self, gapic_api, client): 

193 self._gapic_api = gapic_api 

194 self._client = client 

195 

196 def list_sinks(self, parent, *, max_results=None, page_size=None, page_token=None): 

197 """List sinks for the parent resource. 

198 

199 Args: 

200 parent (str): The parent resource whose sinks are to be listed: 

201 

202 :: 

203 

204 "projects/[PROJECT_ID]" 

205 "organizations/[ORGANIZATION_ID]" 

206 "billingAccounts/[BILLING_ACCOUNT_ID]" 

207 "folders/[FOLDER_ID]". 

208 max_results (Optional[int]): 

209 Optional. The maximum number of entries to return. 

210 Non-positive values are treated as 0. If None, uses API defaults. 

211 page_size (int): number of entries to fetch in each API call. Although 

212 requests are paged internally, logs are returned by the generator 

213 one at a time. If not passed, defaults to a value set by the API. 

214 page_token (str): opaque marker for the starting "page" of entries. If not 

215 passed, the API will return the first page of entries. 

216 

217 Returns: 

218 Generator[~logging_v2.Sink] 

219 """ 

220 request = ListSinksRequest( 

221 parent=parent, page_size=page_size, page_token=page_token 

222 ) 

223 response = self._gapic_api.list_sinks(request) 

224 sink_iter = iter(response) 

225 

226 if max_results is not None and max_results < 0: 

227 raise ValueError("max_results must be positive") 

228 

229 def sinks_pager(sink_iter): 

230 i = 0 

231 for entry in sink_iter: 

232 if max_results is not None and i >= max_results: 

233 break 

234 # Convert the GAPIC sink type into the handwritten `Sink` type 

235 yield Sink.from_api_repr(LogSink.to_dict(entry), client=self._client) 

236 i += 1 

237 

238 return sinks_pager(sink_iter) 

239 

240 def sink_create( 

241 self, parent, sink_name, filter_, destination, *, unique_writer_identity=False 

242 ): 

243 """Create a sink resource. 

244 

245 See 

246 https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/create 

247 

248 Args: 

249 parent(str): The resource in which to create the sink, 

250 including the parent resource and the sink identifier: 

251 

252 :: 

253 

254 "projects/[PROJECT_ID]" 

255 "organizations/[ORGANIZATION_ID]" 

256 "billingAccounts/[BILLING_ACCOUNT_ID]" 

257 "folders/[FOLDER_ID]". 

258 sink_name (str): The name of the sink. 

259 filter_ (str): The advanced logs filter expression defining the 

260 entries exported by the sink. 

261 destination (str): Destination URI for the entries exported by 

262 the sink. 

263 unique_writer_identity (Optional[bool]): determines the kind of 

264 IAM identity returned as writer_identity in the new sink. 

265 

266 Returns: 

267 dict: The sink resource returned from the API (converted from a 

268 protobuf to a dictionary). 

269 """ 

270 sink_pb = LogSink(name=sink_name, filter=filter_, destination=destination) 

271 request = CreateSinkRequest( 

272 parent=parent, sink=sink_pb, unique_writer_identity=unique_writer_identity 

273 ) 

274 created_pb = self._gapic_api.create_sink(request=request) 

275 return MessageToDict( 

276 LogSink.pb(created_pb), 

277 preserving_proto_field_name=False, 

278 ) 

279 

280 def sink_get(self, sink_name): 

281 """Retrieve a sink resource. 

282 

283 Args: 

284 sink_name (str): The resource name of the sink, 

285 including the parent resource and the sink identifier: 

286 

287 :: 

288 

289 "projects/[PROJECT_ID]/sinks/[SINK_ID]" 

290 "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" 

291 "billingAccounts/[BILLING_ACCOUNT_ID]/sinks/[SINK_ID]" 

292 "folders/[FOLDER_ID]/sinks/[SINK_ID]" 

293 

294 Returns: 

295 dict: The sink object returned from the API (converted from a 

296 protobuf to a dictionary). 

297 """ 

298 sink_pb = self._gapic_api.get_sink(sink_name=sink_name) 

299 # NOTE: LogSink message type does not have an ``Any`` field 

300 # so `MessageToDict`` can safely be used. 

301 return MessageToDict( 

302 LogSink.pb(sink_pb), 

303 preserving_proto_field_name=False, 

304 ) 

305 

306 def sink_update( 

307 self, 

308 sink_name, 

309 filter_, 

310 destination, 

311 *, 

312 unique_writer_identity=False, 

313 ): 

314 """Update a sink resource. 

315 

316 Args: 

317 sink_name (str): Required. The resource name of the sink, 

318 including the parent resource and the sink identifier: 

319 

320 :: 

321 

322 "projects/[PROJECT_ID]/sinks/[SINK_ID]" 

323 "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" 

324 "billingAccounts/[BILLING_ACCOUNT_ID]/sinks/[SINK_ID]" 

325 "folders/[FOLDER_ID]/sinks/[SINK_ID]" 

326 filter_ (str): The advanced logs filter expression defining the 

327 entries exported by the sink. 

328 destination (str): destination URI for the entries exported by 

329 the sink. 

330 unique_writer_identity (Optional[bool]): determines the kind of 

331 IAM identity returned as writer_identity in the new sink. 

332 

333 

334 Returns: 

335 dict: The sink resource returned from the API (converted from a 

336 protobuf to a dictionary). 

337 """ 

338 name = sink_name.split("/")[-1] # parse name out of full resource name 

339 sink_pb = LogSink( 

340 name=name, 

341 filter=filter_, 

342 destination=destination, 

343 ) 

344 

345 request = UpdateSinkRequest( 

346 sink_name=sink_name, 

347 sink=sink_pb, 

348 unique_writer_identity=unique_writer_identity, 

349 ) 

350 sink_pb = self._gapic_api.update_sink(request=request) 

351 # NOTE: LogSink message type does not have an ``Any`` field 

352 # so `MessageToDict`` can safely be used. 

353 return MessageToDict( 

354 LogSink.pb(sink_pb), 

355 preserving_proto_field_name=False, 

356 ) 

357 

358 def sink_delete(self, sink_name): 

359 """Delete a sink resource. 

360 

361 Args: 

362 sink_name (str): Required. The full resource name of the sink to delete, 

363 including the parent resource and the sink identifier: 

364 

365 :: 

366 

367 "projects/[PROJECT_ID]/sinks/[SINK_ID]" 

368 "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" 

369 "billingAccounts/[BILLING_ACCOUNT_ID]/sinks/[SINK_ID]" 

370 "folders/[FOLDER_ID]/sinks/[SINK_ID]" 

371 

372 Example: ``"projects/my-project-id/sinks/my-sink-id"``. 

373 """ 

374 self._gapic_api.delete_sink(sink_name=sink_name) 

375 

376 

377class _MetricsAPI(object): 

378 """Helper mapping sink-related APIs.""" 

379 

380 def __init__(self, gapic_api, client): 

381 self._gapic_api = gapic_api 

382 self._client = client 

383 

384 def list_metrics( 

385 self, project, *, max_results=None, page_size=None, page_token=None 

386 ): 

387 """List metrics for the project associated with this client. 

388 

389 Args: 

390 project (str): ID of the project whose metrics are to be listed. 

391 max_results (Optional[int]): 

392 Optional. The maximum number of entries to return. 

393 Non-positive values are treated as 0. If None, uses API defaults. 

394 page_size (int): number of entries to fetch in each API call. Although 

395 requests are paged internally, logs are returned by the generator 

396 one at a time. If not passed, defaults to a value set by the API. 

397 page_token (str): opaque marker for the starting "page" of entries. If not 

398 passed, the API will return the first page of entries. 

399 

400 Returns: 

401 Generator[logging_v2.Metric] 

402 """ 

403 path = f"projects/{project}" 

404 request = ListLogMetricsRequest( 

405 parent=path, 

406 page_size=page_size, 

407 page_token=page_token, 

408 ) 

409 response = self._gapic_api.list_log_metrics(request=request) 

410 metric_iter = iter(response) 

411 

412 if max_results is not None and max_results < 0: 

413 raise ValueError("max_results must be positive") 

414 

415 def metrics_pager(metric_iter): 

416 i = 0 

417 for entry in metric_iter: 

418 if max_results is not None and i >= max_results: 

419 break 

420 # Convert GAPIC metrics type into handwritten `Metric` type 

421 yield Metric.from_api_repr( 

422 LogMetric.to_dict(entry), client=self._client 

423 ) 

424 i += 1 

425 

426 return metrics_pager(metric_iter) 

427 

428 def metric_create(self, project, metric_name, filter_, description): 

429 """Create a metric resource. 

430 

431 See 

432 https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.metrics/create 

433 

434 Args: 

435 project (str): ID of the project in which to create the metric. 

436 metric_name (str): The name of the metric 

437 filter_ (str): The advanced logs filter expression defining the 

438 entries exported by the metric. 

439 description (str): description of the metric. 

440 """ 

441 parent = f"projects/{project}" 

442 metric_pb = LogMetric(name=metric_name, filter=filter_, description=description) 

443 self._gapic_api.create_log_metric(parent=parent, metric=metric_pb) 

444 

445 def metric_get(self, project, metric_name): 

446 """Retrieve a metric resource. 

447 

448 Args: 

449 project (str): ID of the project containing the metric. 

450 metric_name (str): The name of the metric 

451 

452 Returns: 

453 dict: The metric object returned from the API (converted from a 

454 protobuf to a dictionary). 

455 """ 

456 path = f"projects/{project}/metrics/{metric_name}" 

457 metric_pb = self._gapic_api.get_log_metric(metric_name=path) 

458 # NOTE: LogMetric message type does not have an ``Any`` field 

459 # so `MessageToDict`` can safely be used. 

460 return MessageToDict( 

461 LogMetric.pb(metric_pb), 

462 preserving_proto_field_name=False, 

463 ) 

464 

465 def metric_update( 

466 self, 

467 project, 

468 metric_name, 

469 filter_, 

470 description, 

471 ): 

472 """Update a metric resource. 

473 

474 Args: 

475 project (str): ID of the project containing the metric. 

476 metric_name (str): the name of the metric 

477 filter_ (str): the advanced logs filter expression defining the 

478 entries exported by the metric. 

479 description (str): description of the metric. 

480 

481 Returns: 

482 The metric object returned from the API (converted from a 

483 protobuf to a dictionary). 

484 """ 

485 path = f"projects/{project}/metrics/{metric_name}" 

486 metric_pb = LogMetric( 

487 name=path, 

488 filter=filter_, 

489 description=description, 

490 ) 

491 metric_pb = self._gapic_api.update_log_metric( 

492 metric_name=path, metric=metric_pb 

493 ) 

494 # NOTE: LogMetric message type does not have an ``Any`` field 

495 # so `MessageToDict`` can safely be used. 

496 return MessageToDict( 

497 LogMetric.pb(metric_pb), 

498 preserving_proto_field_name=False, 

499 ) 

500 

501 def metric_delete(self, project, metric_name): 

502 """Delete a metric resource. 

503 

504 Args: 

505 project (str): ID of the project containing the metric. 

506 metric_name (str): The name of the metric 

507 """ 

508 path = f"projects/{project}/metrics/{metric_name}" 

509 self._gapic_api.delete_log_metric(metric_name=path) 

510 

511 

512def _parse_log_entry(entry_pb): 

513 """Special helper to parse ``LogEntry`` protobuf into a dictionary. 

514 

515 The ``proto_payload`` field in ``LogEntry`` is of type ``Any``. This 

516 can be problematic if the type URL in the payload isn't in the 

517 ``google.protobuf`` registry. To help with parsing unregistered types, 

518 this function will remove ``proto_payload`` before parsing. 

519 

520 Args: 

521 entry_pb (LogEntry): Log entry protobuf. 

522 

523 Returns: 

524 dict: The parsed log entry. The ``protoPayload`` key may contain 

525 the raw ``Any`` protobuf from ``entry_pb.proto_payload`` if 

526 it could not be parsed. 

527 """ 

528 try: 

529 return MessageToDict( 

530 entry_pb, 

531 preserving_proto_field_name=False, 

532 ) 

533 except TypeError: 

534 if entry_pb.HasField("proto_payload"): 

535 proto_payload = entry_pb.proto_payload 

536 entry_pb.ClearField("proto_payload") 

537 entry_mapping = MessageToDict( 

538 entry_pb, 

539 preserving_proto_field_name=False, 

540 ) 

541 entry_mapping["protoPayload"] = proto_payload 

542 return entry_mapping 

543 else: 

544 raise 

545 

546 

547def _log_entry_mapping_to_pb(mapping): 

548 """Helper for :meth:`write_entries`, et aliae 

549 

550 Performs "impedance matching" between the protobuf attrs and 

551 the keys expected in the JSON API. 

552 """ 

553 entry_pb = LogEntryPB.pb(LogEntryPB()) 

554 # NOTE: We assume ``mapping`` was created in ``Batch.commit`` 

555 # or ``Logger._make_entry_resource``. In either case, if 

556 # the ``protoPayload`` key is present, we assume that the 

557 # type URL is registered with ``google.protobuf`` and will 

558 # not cause any issues in the JSON->protobuf conversion 

559 # of the corresponding ``proto_payload`` in the log entry 

560 # (it is an ``Any`` field). 

561 ParseDict(mapping, entry_pb) 

562 return LogEntryPB(entry_pb) 

563 

564 

565def _client_info_to_gapic(input_info): 

566 """ 

567 Helper function to convert api_core.client_info to 

568 api_core.gapic_v1.client_info subclass 

569 """ 

570 return gapic_v1.client_info.ClientInfo( 

571 python_version=input_info.python_version, 

572 grpc_version=input_info.grpc_version, 

573 api_core_version=input_info.api_core_version, 

574 gapic_version=input_info.gapic_version, 

575 client_library_version=input_info.client_library_version, 

576 user_agent=input_info.user_agent, 

577 rest_version=input_info.rest_version, 

578 ) 

579 

580 

581def make_logging_api(client): 

582 """Create an instance of the Logging API adapter. 

583 

584 Args: 

585 client (~logging_v2.client.Client): The client 

586 that holds configuration details. 

587 

588 Returns: 

589 _LoggingAPI: A metrics API instance with the proper credentials. 

590 """ 

591 info = client._client_info 

592 if isinstance(info, client_info.ClientInfo): 

593 # convert into gapic-compatible subclass 

594 info = _client_info_to_gapic(info) 

595 

596 generated = LoggingServiceV2Client( 

597 credentials=client._credentials, 

598 client_info=info, 

599 client_options=client._client_options, 

600 ) 

601 return _LoggingAPI(generated, client) 

602 

603 

604def make_metrics_api(client): 

605 """Create an instance of the Metrics API adapter. 

606 

607 Args: 

608 client (~logging_v2.client.Client): The client 

609 that holds configuration details. 

610 

611 Returns: 

612 _MetricsAPI: A metrics API instance with the proper credentials. 

613 """ 

614 info = client._client_info 

615 if isinstance(info, client_info.ClientInfo): 

616 # convert into gapic-compatible subclass 

617 info = _client_info_to_gapic(info) 

618 

619 generated = MetricsServiceV2Client( 

620 credentials=client._credentials, 

621 client_info=info, 

622 client_options=client._client_options, 

623 ) 

624 return _MetricsAPI(generated, client) 

625 

626 

627def make_sinks_api(client): 

628 """Create an instance of the Sinks API adapter. 

629 

630 Args: 

631 client (~logging_v2.client.Client): The client 

632 that holds configuration details. 

633 

634 Returns: 

635 _SinksAPI: A metrics API instance with the proper credentials. 

636 """ 

637 info = client._client_info 

638 if isinstance(info, client_info.ClientInfo): 

639 # convert into gapic-compatible subclass 

640 info = _client_info_to_gapic(info) 

641 

642 generated = ConfigServiceV2Client( 

643 credentials=client._credentials, 

644 client_info=info, 

645 client_options=client._client_options, 

646 ) 

647 return _SinksAPI(generated, client)