001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.util; 020 021import com.google.common.collect.Maps; 022import org.apache.hadoop.conf.Configuration; 023import org.apache.oozie.action.hadoop.PasswordMasker; 024import org.apache.oozie.service.ConfigurationService; 025import org.apache.oozie.service.Services; 026 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.HashMap; 031import java.util.LinkedHashMap; 032import java.util.LinkedHashSet; 033import java.util.List; 034import java.util.Map; 035import java.util.Set; 036import java.util.concurrent.ConcurrentHashMap; 037import java.util.concurrent.ScheduledExecutorService; 038import java.util.concurrent.TimeUnit; 039import java.util.concurrent.atomic.AtomicLong; 040import java.util.concurrent.locks.Lock; 041import java.util.concurrent.locks.ReentrantLock; 042 043/** 044 * Instrumentation framework that supports Timers, Counters, Variables and Sampler instrumentation elements. <p> All 045 * instrumentation elements have a group and a name. 046 * @deprecated since 5.0.0 047 */ 048@Deprecated 049public class Instrumentation { 050 private ScheduledExecutorService scheduler; 051 private Lock counterLock; 052 private Lock timerLock; 053 private Lock variableLock; 054 private Lock samplerLock; 055 private Map<String, Map<String, Map<String, Object>>> all; 056 private Map<String, Map<String, Element<Long>>> counters; 057 private Map<String, Map<String, Element<Timer>>> timers; 058 private Map<String, Map<String, Element<Variable>>> variables; 059 private Map<String, Map<String, Element<Double>>> samplers; 060 061 /** 062 * Instrumentation constructor. 063 */ 064 @SuppressWarnings("unchecked") 065 public Instrumentation() { 066 counterLock = new ReentrantLock(); 067 timerLock = new ReentrantLock(); 068 variableLock = new ReentrantLock(); 069 samplerLock = new ReentrantLock(); 070 all = new LinkedHashMap<String, Map<String, Map<String, Object>>>(); 071 counters = new ConcurrentHashMap<String, Map<String, Element<Long>>>(); 072 timers = new ConcurrentHashMap<String, Map<String, Element<Timer>>>(); 073 variables = new ConcurrentHashMap<String, Map<String, Element<Variable>>>(); 074 samplers = new ConcurrentHashMap<String, Map<String, Element<Double>>>(); 075 all.put("variables", (Map<String, Map<String, Object>>) (Object) variables); 076 all.put("samplers", (Map<String, Map<String, Object>>) (Object) samplers); 077 all.put("counters", (Map<String, Map<String, Object>>) (Object) counters); 078 all.put("timers", (Map<String, Map<String, Object>>) (Object) timers); 079 } 080 081 /** 082 * Set the scheduler instance to handle the samplers. 083 * 084 * @param scheduler scheduler instance. 085 */ 086 public void setScheduler(ScheduledExecutorService scheduler) { 087 this.scheduler = scheduler; 088 } 089 090 /** 091 * Cron is a stopwatch that can be started/stopped several times. <p> This class is not thread safe, it does not 092 * need to be. <p> It keeps track of the total time (first start to last stop) and the running time (total time 093 * minus the stopped intervals). <p> Once a Cron is complete it must be added to the corresponding group/name in a 094 * Instrumentation instance. 095 */ 096 public static class Cron { 097 private long start; 098 private long end; 099 private long lapStart; 100 private long own; 101 private long total; 102 private boolean running; 103 104 /** 105 * Creates new Cron, stopped, in zero. 106 */ 107 public Cron() { 108 running = false; 109 } 110 111 /** 112 * Start the cron. It cannot be already started. 113 */ 114 public void start() { 115 if (!running) { 116 if (lapStart == 0) { 117 lapStart = System.currentTimeMillis(); 118 if (start == 0) { 119 start = lapStart; 120 end = start; 121 } 122 } 123 running = true; 124 } 125 } 126 127 /** 128 * Stops the cron. It cannot be already stopped. 129 */ 130 public void stop() { 131 if (running) { 132 end = System.currentTimeMillis(); 133 if (start == 0) { 134 start = end; 135 } 136 total = end - start; 137 if (lapStart > 0) { 138 own += end - lapStart; 139 lapStart = 0; 140 } 141 running = false; 142 } 143 } 144 145 /** 146 * Return the start time of the cron. It must be stopped. 147 * 148 * @return the start time of the cron. 149 */ 150 public long getStart() { 151 if (running) { 152 throw new IllegalStateException("Timer running"); 153 } 154 return start; 155 } 156 157 /** 158 * Return the end time of the cron. It must be stopped. 159 * 160 * @return the end time of the cron. 161 */ 162 public long getEnd() { 163 if (running) { 164 throw new IllegalStateException("Timer running"); 165 } 166 return end; 167 } 168 169 /** 170 * Return the total time of the cron. It must be stopped. 171 * 172 * @return the total time of the cron. 173 */ 174 public long getTotal() { 175 if (running) { 176 throw new IllegalStateException("Timer running"); 177 } 178 return total; 179 } 180 181 /** 182 * Return the own time of the cron. It must be stopped. 183 * 184 * @return the own time of the cron. 185 */ 186 public long getOwn() { 187 if (running) { 188 throw new IllegalStateException("Timer running"); 189 } 190 return own; 191 } 192 193 } 194 195 /** 196 * Gives access to a snapshot of an Instrumentation element (Counter, Timer). <p> Instrumentation element snapshots 197 * are returned by the {@link Instrumentation#getCounters()} and {@link Instrumentation#getTimers()} ()} methods. 198 */ 199 public interface Element<T> { 200 201 /** 202 * Return the snapshot value of the Intrumentation element. 203 * 204 * @return the snapshot value of the Intrumentation element. 205 */ 206 T getValue(); 207 } 208 209 /** 210 * Counter Instrumentation element. 211 */ 212 public static class Counter extends AtomicLong implements Element<Long> { 213 214 /** 215 * Return the counter snapshot. 216 * 217 * @return the counter snapshot. 218 */ 219 public Long getValue() { 220 return get(); 221 } 222 223 /** 224 * Return the String representation of the counter value. 225 * 226 * @return the String representation of the counter value. 227 */ 228 public String toString() { 229 return Long.toString(get()); 230 } 231 232 } 233 234 /** 235 * Timer Instrumentation element. 236 */ 237 public static class Timer implements Element<Timer> { 238 Lock lock = new ReentrantLock(); 239 private long ownTime; 240 private long totalTime; 241 private long ticks; 242 private long ownSquareTime; 243 private long totalSquareTime; 244 private long ownMinTime; 245 private long ownMaxTime; 246 private long totalMinTime; 247 private long totalMaxTime; 248 249 /** 250 * Timer constructor. <p> It is project private for test purposes. 251 */ 252 Timer() { 253 } 254 255 /** 256 * Return the String representation of the timer value. 257 * 258 * @return the String representation of the timer value. 259 */ 260 public String toString() { 261 return XLog.format("ticks[{0}] totalAvg[{1}] ownAvg[{2}]", ticks, getTotalAvg(), getOwnAvg()); 262 } 263 264 /** 265 * Return the timer snapshot. 266 * 267 * @return the timer snapshot. 268 */ 269 public Timer getValue() { 270 try { 271 lock.lock(); 272 Timer timer = new Timer(); 273 timer.ownTime = ownTime; 274 timer.totalTime = totalTime; 275 timer.ticks = ticks; 276 timer.ownSquareTime = ownSquareTime; 277 timer.totalSquareTime = totalSquareTime; 278 timer.ownMinTime = ownMinTime; 279 timer.ownMaxTime = ownMaxTime; 280 timer.totalMinTime = totalMinTime; 281 timer.totalMaxTime = totalMaxTime; 282 return timer; 283 } 284 finally { 285 lock.unlock(); 286 } 287 } 288 289 /** 290 * Add a cron to a timer. <p> It is project private for test purposes. 291 * 292 * @param cron Cron to add. 293 */ 294 void addCron(Cron cron) { 295 try { 296 lock.lock(); 297 long own = cron.getOwn(); 298 long total = cron.getTotal(); 299 ownTime += own; 300 totalTime += total; 301 ticks++; 302 ownSquareTime += own * own; 303 totalSquareTime += total * total; 304 if (ticks == 1) { 305 ownMinTime = own; 306 ownMaxTime = own; 307 totalMinTime = total; 308 totalMaxTime = total; 309 } 310 else { 311 ownMinTime = Math.min(ownMinTime, own); 312 ownMaxTime = Math.max(ownMaxTime, own); 313 totalMinTime = Math.min(totalMinTime, total); 314 totalMaxTime = Math.max(totalMaxTime, total); 315 } 316 } 317 finally { 318 lock.unlock(); 319 } 320 } 321 322 /** 323 * Return the own accumulated computing time by the timer. 324 * 325 * @return own accumulated computing time by the timer. 326 */ 327 public long getOwn() { 328 return ownTime; 329 } 330 331 /** 332 * Return the total accumulated computing time by the timer. 333 * 334 * @return total accumulated computing time by the timer. 335 */ 336 public long getTotal() { 337 return totalTime; 338 } 339 340 /** 341 * Return the number of times a cron was added to the timer. 342 * 343 * @return the number of times a cron was added to the timer. 344 */ 345 public long getTicks() { 346 return ticks; 347 } 348 349 /** 350 * Return the sum of the square own times. <p> It can be used to calculate the standard deviation. 351 * 352 * @return the sum of the square own timer. 353 */ 354 public long getOwnSquareSum() { 355 return ownSquareTime; 356 } 357 358 /** 359 * Return the sum of the square total times. <p> It can be used to calculate the standard deviation. 360 * 361 * @return the sum of the square own timer. 362 */ 363 public long getTotalSquareSum() { 364 return totalSquareTime; 365 } 366 367 /** 368 * Returns the own minimum time. 369 * 370 * @return the own minimum time. 371 */ 372 public long getOwnMin() { 373 return ownMinTime; 374 } 375 376 /** 377 * Returns the own maximum time. 378 * 379 * @return the own maximum time. 380 */ 381 public long getOwnMax() { 382 return ownMaxTime; 383 } 384 385 /** 386 * Returns the total minimum time. 387 * 388 * @return the total minimum time. 389 */ 390 public long getTotalMin() { 391 return totalMinTime; 392 } 393 394 /** 395 * Returns the total maximum time. 396 * 397 * @return the total maximum time. 398 */ 399 public long getTotalMax() { 400 return totalMaxTime; 401 } 402 403 /** 404 * Returns the own average time. 405 * 406 * @return the own average time. 407 */ 408 public long getOwnAvg() { 409 return (ticks != 0) ? ownTime / ticks : 0; 410 } 411 412 /** 413 * Returns the total average time. 414 * 415 * @return the total average time. 416 */ 417 public long getTotalAvg() { 418 return (ticks != 0) ? totalTime / ticks : 0; 419 } 420 421 /** 422 * Returns the total time standard deviation. 423 * 424 * @return the total time standard deviation. 425 */ 426 public double getTotalStdDev() { 427 return evalStdDev(ticks, totalTime, totalSquareTime); 428 } 429 430 /** 431 * Returns the own time standard deviation. 432 * 433 * @return the own time standard deviation. 434 */ 435 public double getOwnStdDev() { 436 return evalStdDev(ticks, ownTime, ownSquareTime); 437 } 438 439 private double evalStdDev(long n, long sn, long ssn) { 440 return (n < 2) ? -1 : Math.sqrt((n * ssn - sn * sn) / (n * (n - 1))); 441 } 442 443 } 444 445 /** 446 * Add a cron to an instrumentation timer. The timer is created if it does not exists. <p> This method is thread 447 * safe. 448 * 449 * @param group timer group. 450 * @param name timer name. 451 * @param cron cron to add to the timer. 452 */ 453 public void addCron(String group, String name, Cron cron) { 454 Map<String, Element<Timer>> map = timers.get(group); 455 if (map == null) { 456 try { 457 timerLock.lock(); 458 map = timers.get(group); 459 if (map == null) { 460 map = new HashMap<String, Element<Timer>>(); 461 timers.put(group, map); 462 } 463 } 464 finally { 465 timerLock.unlock(); 466 } 467 } 468 Timer timer = (Timer) map.get(name); 469 if (timer == null) { 470 try { 471 timerLock.lock(); 472 timer = (Timer) map.get(name); 473 if (timer == null) { 474 timer = new Timer(); 475 map.put(name, timer); 476 } 477 } 478 finally { 479 timerLock.unlock(); 480 } 481 } 482 timer.addCron(cron); 483 } 484 485 /** 486 * Increment an instrumentation counter. The counter is created if it does not exists. <p> This method is thread 487 * safe. 488 * 489 * @param group counter group. 490 * @param name counter name. 491 * @param count increment to add to the counter. 492 */ 493 public void incr(String group, String name, long count) { 494 Map<String, Element<Long>> map = counters.get(group); 495 if (map == null) { 496 try { 497 counterLock.lock(); 498 map = counters.get(group); 499 if (map == null) { 500 map = new HashMap<String, Element<Long>>(); 501 counters.put(group, map); 502 } 503 } 504 finally { 505 counterLock.unlock(); 506 } 507 } 508 Counter counter = (Counter) map.get(name); 509 if (counter == null) { 510 try { 511 counterLock.lock(); 512 counter = (Counter) map.get(name); 513 if (counter == null) { 514 counter = new Counter(); 515 map.put(name, counter); 516 } 517 } 518 finally { 519 counterLock.unlock(); 520 } 521 } 522 counter.addAndGet(count); 523 } 524 525 /** 526 * Decrement an instrumentation counter. The counter is created if it does not exists. <p> This method is thread 527 * safe. 528 * 529 * @param group counter group. 530 * @param name counter name. 531 * @param count decrement to add to the counter. 532 */ 533 public void decr(final String group, final String name, final long count) { 534 incr(group, name, -count); 535 } 536 537 /** 538 * Interface for instrumentation variables. <p> For example a the database service could expose the number of 539 * currently active connections. 540 */ 541 public interface Variable<T> extends Element<T> { 542 } 543 544 /** 545 * Add an instrumentation variable. The variable must not exist. <p> This method is thread safe. 546 * 547 * @param group counter group. 548 * @param name counter name. 549 * @param variable variable to add. 550 */ 551 @SuppressWarnings("unchecked") 552 public void addVariable(String group, String name, Variable variable) { 553 Map<String, Element<Variable>> map = variables.get(group); 554 if (map == null) { 555 try { 556 variableLock.lock(); 557 map = variables.get(group); 558 if (map == null) { 559 map = new HashMap<String, Element<Variable>>(); 560 variables.put(group, map); 561 } 562 } 563 finally { 564 variableLock.unlock(); 565 } 566 } 567 if (map.containsKey(name)) { 568 throw new RuntimeException(XLog.format("Variable group=[{0}] name=[{1}] already defined", group, name)); 569 } 570 map.put(name, variable); 571 } 572 573 /** 574 * Return the JVM system properties. 575 * 576 * @return JVM system properties. 577 */ 578 public Map<String, String> getJavaSystemProperties() { 579 Map<String, String> unmasked = Maps.fromProperties(System.getProperties()); 580 return new PasswordMasker().mask(unmasked); 581 } 582 583 /** 584 * Return the OS environment used to start Oozie. 585 * 586 * @return the OS environment used to start Oozie. 587 */ 588 public Map<String, String> getOSEnv() { 589 Map<String, String> unmasked = System.getenv(); 590 return new PasswordMasker().mask(unmasked); 591 } 592 593 /** 594 * Return the current system configuration as a Map<String,String>. 595 * 596 * @return the current system configuration as a Map<String,String>. 597 */ 598 public Map<String, String> getConfiguration() { 599 final Configuration maskedConf = Services.get().get(ConfigurationService.class).getMaskedConfiguration(); 600 601 return new Map<String, String>() { 602 public int size() { 603 return maskedConf.size(); 604 } 605 606 public boolean isEmpty() { 607 return maskedConf.size() == 0; 608 } 609 610 public boolean containsKey(Object o) { 611 return maskedConf.get((String) o) != null; 612 } 613 614 public boolean containsValue(Object o) { 615 throw new UnsupportedOperationException(); 616 } 617 618 public String get(Object o) { 619 return maskedConf.get((String) o); 620 } 621 622 public String put(String s, String s1) { 623 throw new UnsupportedOperationException(); 624 } 625 626 public String remove(Object o) { 627 throw new UnsupportedOperationException(); 628 } 629 630 public void putAll(Map<? extends String, ? extends String> map) { 631 throw new UnsupportedOperationException(); 632 } 633 634 public void clear() { 635 throw new UnsupportedOperationException(); 636 } 637 638 public Set<String> keySet() { 639 Set<String> set = new LinkedHashSet<String>(); 640 for (Entry<String, String> entry : maskedConf) { 641 set.add(entry.getKey()); 642 } 643 return set; 644 } 645 646 public Collection<String> values() { 647 Set<String> set = new LinkedHashSet<String>(); 648 for (Entry<String, String> entry : maskedConf) { 649 set.add(entry.getValue()); 650 } 651 return set; 652 } 653 654 public Set<Entry<String, String>> entrySet() { 655 Set<Entry<String, String>> set = new LinkedHashSet<Entry<String, String>>(); 656 for (Entry<String, String> entry : maskedConf) { 657 set.add(entry); 658 } 659 return set; 660 } 661 }; 662 } 663 664 /** 665 * Return all the counters. <p> This method is thread safe. <p> The counters are live. The counter value is a 666 * snapshot at the time the {@link Instrumentation.Element#getValue()} is invoked. 667 * 668 * @return all counters. 669 */ 670 public Map<String, Map<String, Element<Long>>> getCounters() { 671 return counters; 672 } 673 674 /** 675 * Return all the timers. <p> This method is thread safe. <p> The timers are live. Once a timer is obtained, all 676 * its values are consistent (they are snapshot at the time the {@link Instrumentation.Element#getValue()} is 677 * invoked. 678 * 679 * @return all counters. 680 */ 681 public Map<String, Map<String, Element<Timer>>> getTimers() { 682 return timers; 683 } 684 685 /** 686 * Return all the variables. <p> This method is thread safe. <p> The variables are live. The variable value is a 687 * snapshot at the time the {@link Instrumentation.Element#getValue()} is invoked. 688 * 689 * @return all counters. 690 */ 691 public Map<String, Map<String, Element<Variable>>> getVariables() { 692 return variables; 693 } 694 695 /** 696 * Return a map containing all variables, counters and timers. 697 * 698 * @return a map containing all variables, counters and timers. 699 */ 700 public Map<String, Map<String, Map<String, Object>>> getAll() { 701 return all; 702 } 703 704 /** 705 * Return the string representation of the instrumentation. 706 * 707 * @return the string representation of the instrumentation. 708 */ 709 public String toString() { 710 String E = System.getProperty("line.separator"); 711 StringBuilder sb = new StringBuilder(4096); 712 for (String element : all.keySet()) { 713 sb.append(element).append(':').append(E); 714 List<String> groups = new ArrayList<String>(all.get(element).keySet()); 715 Collections.sort(groups); 716 for (String group : groups) { 717 sb.append(" ").append(group).append(':').append(E); 718 List<String> names = new ArrayList<String>(all.get(element).get(group).keySet()); 719 Collections.sort(names); 720 for (String name : names) { 721 sb.append(" ").append(name).append(": ").append(((Element) all.get(element). 722 get(group).get(name)).getValue()).append(E); 723 } 724 } 725 } 726 return sb.toString(); 727 } 728 729 private static class Sampler implements Element<Double>, Runnable { 730 private Lock lock = new ReentrantLock(); 731 private int samplingInterval; 732 private Variable<Long> variable; 733 private long[] values; 734 private int current; 735 private long valuesSum; 736 private double rate; 737 738 public Sampler(int samplingPeriod, int samplingInterval, Variable<Long> variable) { 739 this.samplingInterval = samplingInterval; 740 this.variable = variable; 741 values = new long[samplingPeriod / samplingInterval]; 742 valuesSum = 0; 743 current = -1; 744 } 745 746 public int getSamplingInterval() { 747 return samplingInterval; 748 } 749 750 public void run() { 751 try { 752 lock.lock(); 753 long newValue = variable.getValue(); 754 if (current == -1) { 755 valuesSum = newValue; 756 current = 0; 757 values[current] = newValue; 758 } 759 else { 760 current = (current + 1) % values.length; 761 valuesSum = valuesSum - values[current] + newValue; 762 values[current] = newValue; 763 } 764 rate = ((double) valuesSum) / values.length; 765 } 766 finally { 767 lock.unlock(); 768 } 769 } 770 771 public Double getValue() { 772 return rate; 773 } 774 } 775 776 /** 777 * Add a sampling variable. <p> This method is thread safe. 778 * 779 * @param group timer group. 780 * @param name timer name. 781 * @param period sampling period to compute rate. 782 * @param interval sampling frequency, how often the variable is probed. 783 * @param variable variable to sample. 784 */ 785 public void addSampler(String group, String name, int period, int interval, Variable<Long> variable) { 786 if (scheduler == null) { 787 throw new IllegalStateException("scheduler not set, cannot sample"); 788 } 789 try { 790 samplerLock.lock(); 791 Map<String, Element<Double>> map = samplers.get(group); 792 if (map == null) { 793 map = samplers.get(group); 794 if (map == null) { 795 map = new HashMap<String, Element<Double>>(); 796 samplers.put(group, map); 797 } 798 } 799 if (map.containsKey(name)) { 800 throw new RuntimeException(XLog.format("Sampler group=[{0}] name=[{1}] already defined", group, name)); 801 } 802 else { 803 Sampler sampler = new Sampler(period, interval, variable); 804 map.put(name, sampler); 805 scheduler.scheduleAtFixedRate(sampler, 0, sampler.getSamplingInterval(), TimeUnit.SECONDS); 806 } 807 } 808 finally { 809 samplerLock.unlock(); 810 } 811 } 812 813 /** 814 * Return all the samplers. <p> This method is thread safe. <p> The samplers are live. The sampler value is a 815 * snapshot at the time the {@link Instrumentation.Element#getValue()} is invoked. 816 * 817 * @return all counters. 818 */ 819 public Map<String, Map<String, Element<Double>>> getSamplers() { 820 return samplers; 821 } 822 823 public void stop() { 824 825 } 826 827}