1
1
import os
2
-
2
+ import logging
3
3
import functools as ft
4
4
from pathlib import Path
5
5
from dataclasses import dataclass , asdict
13
13
from app .api .deps import CurrentUser
14
14
from app .core .config import settings
15
15
16
+ logger = logging .getLogger (__name__ )
17
+
16
18
17
19
class CloudStorageError (Exception ):
18
20
pass
@@ -21,6 +23,9 @@ class CloudStorageError(Exception):
21
23
class AmazonCloudStorageClient :
22
24
@ft .cached_property
23
25
def client (self ):
26
+ logger .info (
27
+ f"[AmazonCloudStorageClient.client] Initializing S3 client | {{'region': '{ settings .AWS_DEFAULT_REGION } '}}"
28
+ )
24
29
kwargs = {}
25
30
cred_params = (
26
31
("aws_access_key_id" , "AWS_ACCESS_KEY_ID" ),
@@ -31,25 +36,53 @@ def client(self):
31
36
for i , j in cred_params :
32
37
kwargs [i ] = os .environ .get (j , getattr (settings , j ))
33
38
34
- return boto3 .client ("s3" , ** kwargs )
39
+ client = boto3 .client ("s3" , ** kwargs )
40
+ logger .info (
41
+ f"[AmazonCloudStorageClient.client] S3 client initialized | {{'region': '{ settings .AWS_DEFAULT_REGION } '}}"
42
+ )
43
+ return client
35
44
36
45
def create (self ):
46
+ logger .info (
47
+ f"[AmazonCloudStorageClient.create] Checking/creating S3 bucket | {{'bucket': '{ settings .AWS_S3_BUCKET } '}}"
48
+ )
37
49
try :
38
50
# does the bucket exist...
39
51
self .client .head_bucket (Bucket = settings .AWS_S3_BUCKET )
52
+ logger .info (
53
+ f"[AmazonCloudStorageClient.create] Bucket exists | {{'bucket': '{ settings .AWS_S3_BUCKET } '}}"
54
+ )
40
55
except ValueError as err :
56
+ logger .error (
57
+ f"[AmazonCloudStorageClient.create] Invalid bucket configuration | {{'bucket': '{ settings .AWS_S3_BUCKET } ', 'error': '{ str (err )} '}}"
58
+ )
41
59
raise CloudStorageError (err ) from err
42
60
except ClientError as err :
43
61
response = int (err .response ["Error" ]["Code" ])
44
62
if response != 404 :
63
+ logger .error (
64
+ f"[AmazonCloudStorageClient.create] Unexpected AWS error | {{'bucket': '{ settings .AWS_S3_BUCKET } ', 'error': '{ str (err )} ', 'code': { response } }}"
65
+ )
45
66
raise CloudStorageError (err ) from err
46
67
# ... if not create it
47
- self .client .create_bucket (
48
- Bucket = settings .AWS_S3_BUCKET ,
49
- CreateBucketConfiguration = {
50
- "LocationConstraint" : settings .AWS_DEFAULT_REGION ,
51
- },
68
+ logger .warning (
69
+ f"[AmazonCloudStorageClient.create] Bucket not found, creating | {{'bucket': '{ settings .AWS_S3_BUCKET } '}}"
52
70
)
71
+ try :
72
+ self .client .create_bucket (
73
+ Bucket = settings .AWS_S3_BUCKET ,
74
+ CreateBucketConfiguration = {
75
+ "LocationConstraint" : settings .AWS_DEFAULT_REGION ,
76
+ },
77
+ )
78
+ logger .info (
79
+ f"[AmazonCloudStorageClient.create] Bucket created successfully | {{'bucket': '{ settings .AWS_S3_BUCKET } '}}"
80
+ )
81
+ except ClientError as create_err :
82
+ logger .error (
83
+ f"[AmazonCloudStorageClient.create] Failed to create bucket | {{'bucket': '{ settings .AWS_S3_BUCKET } ', 'error': '{ str (create_err )} '}}"
84
+ )
85
+ raise CloudStorageError (create_err ) from create_err
53
86
54
87
55
88
@dataclass (frozen = True )
@@ -61,6 +94,9 @@ def __str__(self):
61
94
return urlunparse (self .to_url ())
62
95
63
96
def to_url (self ):
97
+ logger .info (
98
+ f"[SimpleStorageName.to_url] Generating S3 URL | {{'bucket': '{ self .Bucket } ', 'key': '{ self .Key } '}}"
99
+ )
64
100
kwargs = {
65
101
"scheme" : "s3" ,
66
102
"netloc" : self .Bucket ,
@@ -69,21 +105,35 @@ def to_url(self):
69
105
for k in ParseResult ._fields :
70
106
kwargs .setdefault (k )
71
107
72
- return ParseResult (** kwargs )
108
+ url = ParseResult (** kwargs )
109
+ logger .info (
110
+ f"[SimpleStorageName.to_url] S3 URL generated | {{'url': '{ urlunparse (url )} '}}"
111
+ )
112
+ return url
73
113
74
114
@classmethod
75
115
def from_url (cls , url : str ):
116
+ logger .info (
117
+ f"[SimpleStorageName.from_url] Parsing S3 URL | {{'url': '{ url } '}}"
118
+ )
76
119
url = urlparse (url )
77
120
path = Path (url .path )
78
121
if path .is_absolute ():
79
122
path = path .relative_to (path .root )
80
123
81
- return cls (Bucket = url .netloc , Key = str (path ))
124
+ instance = cls (Bucket = url .netloc , Key = str (path ))
125
+ logger .info (
126
+ f"[SimpleStorageName.from_url] URL parsed successfully | {{'bucket': '{ instance .Bucket } ', 'key': '{ instance .Key } '}}"
127
+ )
128
+ return instance
82
129
83
130
84
131
class CloudStorage :
85
132
def __init__ (self , user : CurrentUser ):
86
133
self .user = user
134
+ logger .info (
135
+ f"[CloudStorage.init] Initialized CloudStorage | {{'user_id': '{ user .id } '}}"
136
+ )
87
137
88
138
def put (self , source : UploadFile , basename : str ):
89
139
raise NotImplementedError ()
@@ -96,8 +146,14 @@ class AmazonCloudStorage(CloudStorage):
96
146
def __init__ (self , user : CurrentUser ):
97
147
super ().__init__ (user )
98
148
self .aws = AmazonCloudStorageClient ()
149
+ logger .info (
150
+ f"[AmazonCloudStorage.init] Initialized AmazonCloudStorage | {{'user_id': '{ user .id } '}}"
151
+ )
99
152
100
153
def put (self , source : UploadFile , basename : Path ) -> SimpleStorageName :
154
+ logger .info (
155
+ f"[AmazonCloudStorage.put] Starting file upload | {{'user_id': '{ self .user .id } ', 'filename': '{ source .filename } ', 'basename': '{ basename } '}}"
156
+ )
101
157
key = Path (str (self .user .id ), basename )
102
158
destination = SimpleStorageName (str (key ))
103
159
@@ -111,16 +167,29 @@ def put(self, source: UploadFile, basename: Path) -> SimpleStorageName:
111
167
},
112
168
** kwargs ,
113
169
)
170
+ logger .info (
171
+ f"[AmazonCloudStorage.put] File uploaded successfully | {{'user_id': '{ self .user .id } ', 'bucket': '{ destination .Bucket } ', 'key': '{ destination .Key } '}}"
172
+ )
114
173
except ClientError as err :
174
+ logger .error (
175
+ f"[AmazonCloudStorage.put] AWS upload error | {{'user_id': '{ self .user .id } ', 'bucket': '{ destination .Bucket } ', 'key': '{ destination .Key } ', 'error': '{ str (err )} '}}"
176
+ )
115
177
raise CloudStorageError (f'AWS Error: "{ err } "' ) from err
116
178
117
179
return destination
118
180
119
181
def stream (self , url : str ) -> StreamingBody :
182
+ logger .info (
183
+ f"[AmazonCloudStorage.stream] Starting file stream | {{'user_id': '{ self .user .id } ', 'url': '{ url } '}}"
184
+ )
120
185
name = SimpleStorageName .from_url (url )
121
186
kwargs = asdict (name )
122
187
try :
123
- return self .aws .client .get_object (** kwargs ).get ("Body" )
188
+ body = self .aws .client .get_object (** kwargs ).get ("Body" )
189
+ logger .info (
190
+ f"[AmazonCloudStorage.stream] File streamed successfully | {{'user_id': '{ self .user .id } ', 'bucket': '{ name .Bucket } ', 'key': '{ name .Key } '}}"
191
+ )
192
+ return body
124
193
except ClientError as err :
125
194
raise CloudStorageError (f'AWS Error: "{ err } " ({ url } )' ) from err
126
195
0 commit comments