1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.mapreduce.v2.app.webapp; 20 21 import static org.apache.hadoop.mapreduce.v2.app.webapp.AMParams.JOB_ID; 22 import static org.apache.hadoop.mapreduce.v2.app.webapp.AMParams.TASK_ID; 23 import static org.apache.hadoop.yarn.webapp.view.JQueryUI.C_TABLE; 24 import static org.apache.hadoop.yarn.webapp.view.JQueryUI._INFO_WRAP; 25 26 import java.util.Map; 27 28 import org.apache.hadoop.mapreduce.Counter; 29 import org.apache.hadoop.mapreduce.CounterGroup; 30 import org.apache.hadoop.mapreduce.Counters; 31 import org.apache.hadoop.mapreduce.v2.api.records.JobId; 32 import org.apache.hadoop.mapreduce.v2.api.records.TaskId; 33 import org.apache.hadoop.mapreduce.v2.app.AppContext; 34 import org.apache.hadoop.mapreduce.v2.app.job.Job; 35 import org.apache.hadoop.mapreduce.v2.app.job.Task; 36 import org.apache.hadoop.mapreduce.v2.util.MRApps; 37 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; 38 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV; 39 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TABLE; 40 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TBODY; 41 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TD; 42 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.THEAD; 43 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TR; 44 import org.apache.hadoop.yarn.webapp.view.HtmlBlock; 45 46 import com.google.inject.Inject; 47 48 public class CountersBlock extends HtmlBlock { 49 Job job; 50 Task task; 51 Counters total; 52 Counters map; 53 Counters reduce; 54 CountersBlock(AppContext appCtx, ViewContext ctx)55 @Inject CountersBlock(AppContext appCtx, ViewContext ctx) { 56 super(ctx); 57 getCounters(appCtx); 58 } 59 render(Block html)60 @Override protected void render(Block html) { 61 if (job == null) { 62 html. 63 p()._("Sorry, no counters for nonexistent", $(JOB_ID, "job"))._(); 64 return; 65 } 66 if (!$(TASK_ID).isEmpty() && task == null) { 67 html. 68 p()._("Sorry, no counters for nonexistent", $(TASK_ID, "task"))._(); 69 return; 70 } 71 72 if(total == null || total.getGroupNames() == null || total.countCounters() == 0) { 73 String type = $(TASK_ID); 74 if(type == null || type.isEmpty()) { 75 type = $(JOB_ID, "the job"); 76 } 77 html. 78 p()._("Sorry it looks like ",type," has no counters.")._(); 79 return; 80 } 81 82 String urlBase; 83 String urlId; 84 if(task != null) { 85 urlBase = "singletaskcounter"; 86 urlId = MRApps.toString(task.getID()); 87 } else { 88 urlBase = "singlejobcounter"; 89 urlId = MRApps.toString(job.getID()); 90 } 91 92 93 int numGroups = 0; 94 TBODY<TABLE<DIV<Hamlet>>> tbody = html. 95 div(_INFO_WRAP). 96 table("#counters"). 97 thead(). 98 tr(). 99 th(".group.ui-state-default", "Counter Group"). 100 th(".ui-state-default", "Counters")._()._(). 101 tbody(); 102 for (CounterGroup g : total) { 103 CounterGroup mg = map == null ? null : map.getGroup(g.getName()); 104 CounterGroup rg = reduce == null ? null : reduce.getGroup(g.getName()); 105 ++numGroups; 106 // This is mostly for demonstration :) Typically we'd introduced 107 // a CounterGroup block to reduce the verbosity. OTOH, this 108 // serves as an indicator of where we're in the tag hierarchy. 109 TR<THEAD<TABLE<TD<TR<TBODY<TABLE<DIV<Hamlet>>>>>>>> groupHeadRow = tbody. 110 tr(). 111 th().$title(g.getName()).$class("ui-state-default"). 112 _(fixGroupDisplayName(g.getDisplayName()))._(). 113 td().$class(C_TABLE). 114 table(".dt-counters").$id(job.getID()+"."+g.getName()). 115 thead(). 116 tr().th(".name", "Name"); 117 118 if (map != null) { 119 groupHeadRow.th("Map").th("Reduce"); 120 } 121 // Ditto 122 TBODY<TABLE<TD<TR<TBODY<TABLE<DIV<Hamlet>>>>>>> group = groupHeadRow. 123 th(map == null ? "Value" : "Total")._()._(). 124 tbody(); 125 for (Counter counter : g) { 126 // Ditto 127 TR<TBODY<TABLE<TD<TR<TBODY<TABLE<DIV<Hamlet>>>>>>>> groupRow = group. 128 tr(); 129 if (task == null && mg == null && rg == null) { 130 groupRow.td().$title(counter.getName())._(counter.getDisplayName()). 131 _(); 132 } else { 133 groupRow.td().$title(counter.getName()). 134 a(url(urlBase,urlId,g.getName(), 135 counter.getName()), counter.getDisplayName()). 136 _(); 137 } 138 if (map != null) { 139 Counter mc = mg == null ? null : mg.findCounter(counter.getName()); 140 Counter rc = rg == null ? null : rg.findCounter(counter.getName()); 141 groupRow. 142 td(mc == null ? "0" : String.format("%,d", mc.getValue())). 143 td(rc == null ? "0" : String.format("%,d", rc.getValue())); 144 } 145 groupRow.td(String.format("%,d", counter.getValue()))._(); 146 } 147 group._()._()._()._(); 148 } 149 tbody._()._()._(); 150 } 151 getCounters(AppContext ctx)152 private void getCounters(AppContext ctx) { 153 JobId jobID = null; 154 TaskId taskID = null; 155 String tid = $(TASK_ID); 156 if (!tid.isEmpty()) { 157 taskID = MRApps.toTaskID(tid); 158 jobID = taskID.getJobId(); 159 } else { 160 String jid = $(JOB_ID); 161 if (jid != null && !jid.isEmpty()) { 162 jobID = MRApps.toJobID(jid); 163 } 164 } 165 if (jobID == null) { 166 return; 167 } 168 job = ctx.getJob(jobID); 169 if (job == null) { 170 return; 171 } 172 if (taskID != null) { 173 task = job.getTask(taskID); 174 if (task == null) { 175 return; 176 } 177 total = task.getCounters(); 178 return; 179 } 180 // Get all types of counters 181 Map<TaskId, Task> tasks = job.getTasks(); 182 total = job.getAllCounters(); 183 boolean needTotalCounters = false; 184 if (total == null) { 185 total = new Counters(); 186 needTotalCounters = true; 187 } 188 map = new Counters(); 189 reduce = new Counters(); 190 for (Task t : tasks.values()) { 191 Counters counters = t.getCounters(); 192 if (counters == null) { 193 continue; 194 } 195 switch (t.getType()) { 196 case MAP: map.incrAllCounters(counters); break; 197 case REDUCE: reduce.incrAllCounters(counters); break; 198 } 199 if (needTotalCounters) { 200 total.incrAllCounters(counters); 201 } 202 } 203 } 204 fixGroupDisplayName(CharSequence name)205 private String fixGroupDisplayName(CharSequence name) { 206 return name.toString().replace(".", ".\u200B").replace("$", "\u200B$"); 207 } 208 }