Skip to content

Commit e8b9462

Browse files
author
zhongzichao
authored
add log client (#947) (#950)
* add log client
1 parent 51e1ebe commit e8b9462

File tree

5 files changed

+165
-3
lines changed

5 files changed

+165
-3
lines changed

client/paddleflow/cli/log.py

+69
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,43 @@ def show(ctx, runid, jobid=None, pagesize=None, pageno=None, logfileposition=Non
6464
sys.exit(1)
6565

6666

67+
@log.command(context_settings=dict(max_content_width=2000), cls=command_required_option_from_option())
68+
@click.option('-j', '--jobid', help="jobid and name is one of required field")
69+
@click.option('-n', '--name', help="name")
70+
@click.option('-ns', '--namespace', help="namespace")
71+
@click.option('-c', '--clustername', help="clustername")
72+
@click.option('-r', '--readfromtail', help="read logs from tail, set value means yes")
73+
@click.option('-l', '--line_limit', help="line_limit, default is 1000")
74+
@click.option('-s', '--size_limit', help="size_limit, default is 100MB")
75+
@click.option('-t', '--type', help="type in deploy or pod")
76+
@click.option('-f', '--framework', help="job framework")
77+
@click.pass_context
78+
def showlimit(ctx, jobid=None, name=None, namespace=None, clustername=None, readfromtail=None, line_limit=None, size_limit=None, type=None, framework=None):
79+
"""
80+
show job,deployment,pod log\n
81+
if jobid is null, it would return deployment or pod logs by <namespace, name, type>
82+
else it would return PF job logs by jobid
83+
84+
When both A and B have values, the minimum value is selected to return to the log
85+
"""
86+
client = ctx.obj['client']
87+
output_format = 'text'
88+
if jobid:
89+
click.echo("query job by [jobid]".format(jobid))
90+
elif name:
91+
click.echo("query log by [namespace/name]".format(namespace, name))
92+
93+
valid, response = client.show_log_by_limit(job_id=jobid, name=name, namespace=namespace, cluster_name=clustername, read_from_tail=readfromtail,
94+
line_limit=line_limit, size_limit=size_limit, type=type, framework=framework)
95+
if valid:
96+
# if jobid is None and len(response['runLog']) > 0:
97+
# response['runLog'] = [response['runLog'][0]]
98+
_print_log_by_limit(response, output_format)
99+
else:
100+
click.echo("show logs failed with message[%s]" % response)
101+
sys.exit(1)
102+
103+
67104
def _print_run_log(loginfo, out_format):
68105
"""print run log """
69106
submit_loginfo = loginfo['submitLog']
@@ -84,3 +121,35 @@ def _print_run_log(loginfo, out_format):
84121
if index != len(run_loginfo) - 1:
85122
data.append(['\n'])
86123
print_output(data, [], out_format)
124+
125+
def _print_log_by_limit(loginfo, out_format):
126+
"""print run log by limit """
127+
eventList = loginfo['eventList']
128+
logs = loginfo['logs']
129+
130+
if eventList is None or len(eventList) == 0:
131+
click.echo(click.style('eventList is None', fg='green'))
132+
else:
133+
click.echo(click.style('eventList:', fg='green'))
134+
for index, item in enumerate(eventList):
135+
click.echo(item)
136+
137+
click.echo(click.style('logs:', fg='green'))
138+
139+
data = []
140+
if len(logs) == 0:
141+
data.append(["None"])
142+
for index, item in enumerate(logs):
143+
if item.job_id:
144+
line = ["jobid:" + item.job_id, "taskid:" + item.task_id, "has_next_page:" + str(item.has_next_page),
145+
"truncated:" + str(item.truncated)]
146+
else:
147+
line = ["name:" + item.name, "taskid:" + item.task_id, "has_next_page:" + str(item.has_next_page),
148+
"line_limit:" + str(item.line_limit), "size_limit:" + str(item.size_limit), "truncated:" + str(item.truncated)]
149+
line_log = []
150+
line_log.append(item.log_content)
151+
data.append(line)
152+
data.append(line_log)
153+
if index != len(logs) - 1:
154+
data.append(['\n'])
155+
print_output(data, [], out_format)

client/paddleflow/client.py

+13
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,19 @@ def show_log(self, run_id, job_id=None, page_size=None, page_no=None, log_file_p
714714
return LogServiceApi.get_log_info(self.paddleflow_server, run_id, job_id, page_size, page_no, log_file_position,
715715
self.header)
716716

717+
def show_log_by_limit(self, job_id=None, name=None, namespace=None, cluster_name=None, read_from_tail=None,
718+
line_limit=None, size_limit=None,
719+
type=None, framework=None):
720+
"""
721+
show job logs or kubernetes logs or others
722+
"""
723+
self.pre_check()
724+
return LogServiceApi.get_log_info_by_limit(self.paddleflow_server, job_id=job_id, name=name, namespace=namespace,
725+
cluster_name=cluster_name, read_from_tail=read_from_tail, line_limit=line_limit, size_limit=size_limit,
726+
type=type, framework=framework, header=self.header
727+
# job_id=job_id,
728+
)
729+
717730
def create_job(self, job_type, job_request):
718731
"""
719732
create_job

client/paddleflow/common/api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
PADDLE_FLOW_ARTIFACT = '/api/paddleflow/v%d/artifact' % PADDLE_FLOW_VERSION
3434
PADDLE_FLOW_SCHEDULE = '/api/paddleflow/v%d/schedule' % PADDLE_FLOW_VERSION
3535
PADDLE_FLOW_FLAVOUR = '/api/paddleflow/v%d/flavour' % PADDLE_FLOW_VERSION
36-
PADDLE_FLOW_LOG = '/api/paddleflow/v%d/log/run' % PADDLE_FLOW_VERSION
36+
PADDLE_FLOW_LOG = '/api/paddleflow/v%d/log' % PADDLE_FLOW_VERSION
3737
PADDLE_FLOW_JOB = '/api/paddleflow/v%d/job' % PADDLE_FLOW_VERSION
3838
PADDLE_FLOW_STATISTIC = '/api/paddleflow/v%d/statistics' % PADDLE_FLOW_VERSION
3939
PADDLE_FLOW_SERVER_VERSION = '/api/paddleflow/v%d/version' % PADDLE_FLOW_VERSION

client/paddleflow/log/log_api.py

+59-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121

2222
from paddleflow.common import api
2323
from paddleflow.common.exception.paddleflow_sdk_exception import PaddleFlowSDKException
24-
from paddleflow.log.log_info import LogInfo
24+
from paddleflow.log.log_info import LogInfo, LogInfoByLimit
2525
from paddleflow.utils import api_client
2626

2727
DEFAULT_PAGESIZE = 100
2828
DEFAULT_PAGENO = 1
29+
DEFAULT_LINE_LIMIT = 1000
30+
DEFAULT_SIZE_LIMIT = "100KB"
2931

3032
class LogServiceApi(object):
3133
"""
@@ -51,7 +53,7 @@ def get_log_info(self, host, runid, jobid, pagesize, pageno, fileposition, heade
5153
params['pageNo'] = pageno
5254
if fileposition:
5355
params['logFilePosition'] = fileposition
54-
response = api_client.call_api(method="GET", url=parse.urljoin(host, api.PADDLE_FLOW_LOG + "/%s" % runid),
56+
response = api_client.call_api(method="GET", url=parse.urljoin(host, api.PADDLE_FLOW_LOG + "/run/%s" % runid),
5557
headers=header, params=params)
5658
if not response:
5759
raise PaddleFlowSDKException("Connection Error", "get log failed due to HTTPError")
@@ -77,3 +79,58 @@ def get_log_info(self, host, runid, jobid, pagesize, pageno, fileposition, heade
7779
return True, log_info_dict
7880

7981

82+
@classmethod
83+
def get_log_info_by_limit(self, host, job_id=None, name=None, namespace=None,
84+
cluster_name=None, read_from_tail=None, line_limit=None, size_limit=None,
85+
type=None, framework=None, header=None):
86+
""" get logs by line_limit or size_limit
87+
"""
88+
if not header:
89+
raise PaddleFlowSDKException("InvalidRequest", "paddleflow should login first")
90+
params = {}
91+
if job_id:
92+
params['jobID'] = job_id
93+
if name:
94+
params['name'] = name
95+
if namespace:
96+
params['namespace'] = namespace
97+
if cluster_name:
98+
params['clusterName'] = cluster_name
99+
if read_from_tail:
100+
params['readFromTail'] = read_from_tail
101+
if line_limit:
102+
params['lineLimit'] = str(line_limit)
103+
if size_limit:
104+
params['sizeLimit'] = str(size_limit)
105+
if type:
106+
params['type'] = type
107+
if framework:
108+
params['framework'] = framework
109+
110+
response = api_client.call_api(method="GET", url=parse.urljoin(host, api.PADDLE_FLOW_LOG + "/job"),
111+
headers=header, params=params)
112+
if not response:
113+
raise PaddleFlowSDKException("Connection Error", "get log failed due to HTTPError")
114+
data = json.loads(response.text)
115+
if 'message' in data:
116+
return False, data['message']
117+
if line_limit is None:
118+
line_limit = DEFAULT_LINE_LIMIT
119+
else:
120+
line_limit = int(line_limit)
121+
if size_limit is None:
122+
size_limit = DEFAULT_SIZE_LIMIT
123+
124+
125+
loginfo_list = []
126+
for task in data['taskList']:
127+
loginfo = LogInfoByLimit(job_id=data['jobID'], task_id=task['taskID'], name=data['name'],
128+
has_next_page=task['logInfo']['hasNextPage'],
129+
truncated=task['logInfo']['truncated'],
130+
line_limit=line_limit, size_limit=size_limit, log_content=task['logInfo']['logContent'])
131+
loginfo_list.append(loginfo)
132+
133+
log_info_dict = {'eventList': data['eventList'], 'logs': loginfo_list}
134+
return True, log_info_dict
135+
136+

client/paddleflow/log/log_info.py

+23
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,26 @@ def __init__(self, runid, jobid, taskid, has_next_page, truncated, pagesize, pag
3939
self.pageno = pageno
4040
# 具体的日志内容
4141
self.log_content = log_content
42+
43+
44+
class LogInfoByLimit(object):
45+
46+
"""the class of log info"""
47+
def __init__(self, job_id="", task_id="", name="", has_next_page=False, truncated=False, line_limit=0, size_limit="", log_content=""):
48+
"""init """
49+
# job id
50+
self.job_id = job_id
51+
# job下子task的id
52+
self.task_id = task_id
53+
# name
54+
self.name = name
55+
# 日志内容是否还有下一页,为true时则有下一页,否则为最后一页
56+
self.has_next_page = has_next_page
57+
# 日志内容是否被截断,为true时则被截断,否则未截断
58+
self.truncated = truncated
59+
# 行数限制
60+
self.line_limit = line_limit
61+
# 字节数限制
62+
self.size_limit = size_limit
63+
# 具体的日志内容
64+
self.log_content = log_content

0 commit comments

Comments
 (0)